Project

General

Profile

Download (77.8 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

    
10
package eu.etaxonomy.cdm.api.service;
11

    
12
import java.io.IOException;
13
import java.net.URI;
14
import java.net.URISyntaxException;
15
import java.util.ArrayList;
16
import java.util.Arrays;
17
import java.util.Collection;
18
import java.util.Collections;
19
import java.util.HashMap;
20
import java.util.HashSet;
21
import java.util.Iterator;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.Map.Entry;
25
import java.util.Set;
26
import java.util.UUID;
27

    
28
import org.apache.log4j.Logger;
29
import org.apache.lucene.queryparser.classic.ParseException;
30
import org.apache.lucene.search.BooleanClause.Occur;
31
import org.apache.lucene.search.BooleanQuery.Builder;
32
import org.apache.lucene.search.SortField;
33
import org.apache.lucene.search.grouping.TopGroups;
34
import org.apache.lucene.util.BytesRef;
35
import org.hibernate.TransientObjectException;
36
import org.hibernate.search.spatial.impl.Rectangle;
37
import org.joda.time.Partial;
38
import org.springframework.beans.factory.annotation.Autowired;
39
import org.springframework.dao.DataRetrievalFailureException;
40
import org.springframework.stereotype.Service;
41
import org.springframework.transaction.annotation.Transactional;
42

    
43
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
44
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeConfigurator;
45
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
46
import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
47
import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
48
import eu.etaxonomy.cdm.api.service.config.FindOccurrencesConfigurator;
49
import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
50
import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
51
import eu.etaxonomy.cdm.api.service.dto.DerivateDTO;
52
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO;
53
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.ContigFile;
54
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.Link;
55
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.MolecularData;
56
import eu.etaxonomy.cdm.api.service.dto.FieldUnitDTO;
57
import eu.etaxonomy.cdm.api.service.dto.PreservedSpecimenDTO;
58
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
59
import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
60
import eu.etaxonomy.cdm.api.service.pager.Pager;
61
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
62
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
63
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
64
import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
65
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
66
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
67
import eu.etaxonomy.cdm.api.service.search.SearchResult;
68
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
69
import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
70
import eu.etaxonomy.cdm.common.CdmUtils;
71
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
72
import eu.etaxonomy.cdm.format.CdmFormatterFactory;
73
import eu.etaxonomy.cdm.format.ICdmFormatter.FormatKey;
74
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
75
import eu.etaxonomy.cdm.model.CdmBaseType;
76
import eu.etaxonomy.cdm.model.agent.AgentBase;
77
import eu.etaxonomy.cdm.model.common.CdmBase;
78
import eu.etaxonomy.cdm.model.common.DefinedTerm;
79
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
80
import eu.etaxonomy.cdm.model.common.Language;
81
import eu.etaxonomy.cdm.model.description.CategoricalData;
82
import eu.etaxonomy.cdm.model.description.DescriptionBase;
83
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
84
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
85
import eu.etaxonomy.cdm.model.description.QuantitativeData;
86
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
87
import eu.etaxonomy.cdm.model.description.TaxonDescription;
88
import eu.etaxonomy.cdm.model.location.Country;
89
import eu.etaxonomy.cdm.model.location.NamedArea;
90
import eu.etaxonomy.cdm.model.media.Media;
91
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
92
import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
93
import eu.etaxonomy.cdm.model.media.MediaUtils;
94
import eu.etaxonomy.cdm.model.molecular.AmplificationResult;
95
import eu.etaxonomy.cdm.model.molecular.DnaSample;
96
import eu.etaxonomy.cdm.model.molecular.Sequence;
97
import eu.etaxonomy.cdm.model.molecular.SingleRead;
98
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
99
import eu.etaxonomy.cdm.model.name.TaxonName;
100
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
101
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
102
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
103
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
104
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
105
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
106
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
107
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
108
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
109
import eu.etaxonomy.cdm.model.taxon.Taxon;
110
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
111
import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
112
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
113
import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
114
import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
115
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
116
import eu.etaxonomy.cdm.persistence.query.AssignmentStatus;
117
import eu.etaxonomy.cdm.persistence.query.OrderHint;
118
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
119
import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
120

    
121
/**
122
 * @author a.babadshanjan
123
 * @since 01.09.2008
124
 */
125
@Service
126
@Transactional(readOnly = true)
127
public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase, IOccurrenceDao> implements IOccurrenceService {
128

    
129
    static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
130

    
131
    @Autowired
132
    private IDefinedTermDao definedTermDao;
133

    
134
    @Autowired
135
    private IDescriptionService descriptionService;
136

    
137
    @Autowired
138
    private INameService nameService;
139

    
140
    @Autowired
141
    private IEventBaseService eventService;
142

    
143
    @Autowired
144
    private ITaxonService taxonService;
145

    
146
    @Autowired
147
    private ISequenceService sequenceService;
148

    
149
    @Autowired
150
    private AbstractBeanInitializer beanInitializer;
151

    
152
    @Autowired
153
    private ILuceneIndexToolProvider luceneIndexToolProvider;
154

    
155
    private static final String SEPARATOR_STRING = ", ";
156

    
157
    public OccurrenceServiceImpl() {
158
        logger.debug("Load OccurrenceService Bean");
159
    }
160

    
161

    
162
    @Override
163
    @Transactional(readOnly = false)
164
    public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
165
        if (clazz == null) {
166
            clazz = SpecimenOrObservationBase.class;
167
        }
168
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
169
    }
170

    
171
    /**
172
     * FIXME Candidate for harmonization
173
     * move to termService
174
     */
175
    @Override
176
    public Country getCountryByIso(String iso639) {
177
        return this.definedTermDao.getCountryByIso(iso639);
178

    
179
    }
180

    
181
    /**
182
     * FIXME Candidate for harmonization
183
     * move to termService
184
     */
185
    @Override
186
    public List<Country> getCountryByName(String name) {
187
        List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
188
        List<Country> countries = new ArrayList<>();
189
        for (int i = 0; i < terms.size(); i++) {
190
            countries.add((Country) terms.get(i));
191
        }
192
        return countries;
193
    }
194

    
195
    @Override
196
    @Autowired
197
    protected void setDao(IOccurrenceDao dao) {
198
        this.dao = dao;
199
    }
200

    
201
    @Override
202
    public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
203
        long numberOfResults = dao.countDerivationEvents(occurence);
204

    
205
        List<DerivationEvent> results = new ArrayList<>();
206
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
207
            results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
208
        }
209

    
210
        return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
211
    }
212

    
213
    @Override
214
    public long countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
215
        return dao.countDeterminations(occurence, taxonbase);
216
    }
217

    
218
    @Override
219
    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase,
220
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
221
        long numberOfResults = dao.countDeterminations(occurrence, taxonBase);
222

    
223
        List<DeterminationEvent> results = new ArrayList<>();
224
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
225
            results = dao.getDeterminations(occurrence, taxonBase, pageSize, pageNumber, propertyPaths);
226
        }
227

    
228
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
229
    }
230

    
231
    @Override
232
    public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
233
        long numberOfResults = dao.countMedia(occurence);
234

    
235
        List<Media> results = new ArrayList<>();
236
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
237
            results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
238
        }
239

    
240
        return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
241
    }
242

    
243
    @Override
244
    public Pager<Media> getMediainHierarchy(SpecimenOrObservationBase rootOccurence, Integer pageSize,
245
            Integer pageNumber, List<String> propertyPaths) {
246
        List<Media> media = new ArrayList<>();
247
        //media specimens
248
        if(rootOccurence.isInstanceOf(MediaSpecimen.class)){
249
            MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(rootOccurence, MediaSpecimen.class);
250
            media.add(mediaSpecimen.getMediaSpecimen());
251
        }
252
        // pherograms & gelPhotos
253
        if (rootOccurence.isInstanceOf(DnaSample.class)) {
254
            DnaSample dnaSample = CdmBase.deproxy(rootOccurence, DnaSample.class);
255
            Set<Sequence> sequences = dnaSample.getSequences();
256
            //we do show only those gelPhotos which lead to a consensus sequence
257
            for (Sequence sequence : sequences) {
258
                Set<Media> dnaRelatedMedia = new HashSet<>();
259
                for (SingleRead singleRead : sequence.getSingleReads()){
260
                    AmplificationResult amplification = singleRead.getAmplificationResult();
261
                    dnaRelatedMedia.add(amplification.getGelPhoto());
262
                    dnaRelatedMedia.add(singleRead.getPherogram());
263
                    dnaRelatedMedia.remove(null);
264
                }
265
                media.addAll(dnaRelatedMedia);
266
            }
267
        }
268
        if(rootOccurence.isInstanceOf(DerivedUnit.class)){
269
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(rootOccurence, DerivedUnit.class);
270
            for (DerivationEvent derivationEvent : derivedUnit.getDerivationEvents()) {
271
                for (DerivedUnit childDerivative : derivationEvent.getDerivatives()) {
272
                    media.addAll(getMediainHierarchy(childDerivative, pageSize, pageNumber, propertyPaths).getRecords());
273
                }
274
            }
275
        }
276
        return new DefaultPagerImpl<Media>(pageNumber, Long.valueOf(media.size()), pageSize, media);
277
    }
278

    
279
    @Override
280
    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonName determinedAs,
281
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
282
        long numberOfResults = dao.count(type, determinedAs);
283
        @SuppressWarnings("rawtypes")
284
        List<SpecimenOrObservationBase> results = new ArrayList<>();
285
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
286
            results = dao.list(type, determinedAs, pageSize, pageNumber, orderHints, propertyPaths);
287
        }
288
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
289
    }
290

    
291
    @Override
292
    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs,
293
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
294
        long numberOfResults = dao.count(type, determinedAs);
295
        @SuppressWarnings("rawtypes")
296
        List<SpecimenOrObservationBase> results = new ArrayList<>();
297
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
298
            results = dao.list(type, determinedAs, pageSize, pageNumber, orderHints, propertyPaths);
299
        }
300
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
301
    }
302

    
303
    @Override
304
    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache(Integer limit, String pattern) {
305
        return dao.getDerivedUnitUuidAndTitleCache(limit, pattern);
306
    }
307

    
308
    @Override
309
    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
310
        return dao.getFieldUnitUuidAndTitleCache();
311
    }
312

    
313
    @Override
314
    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
315
        derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
316
        DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
317
        config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
318
        DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
319
        beanInitializer.initialize(derivedUnitFacade, propertyPaths);
320
        return derivedUnitFacade;
321
    }
322

    
323
    @Override
324
    public List<DerivedUnitFacade> listDerivedUnitFacades(
325
            DescriptionBase description, List<String> propertyPaths) {
326

    
327
        List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<>();
328
        IndividualsAssociation tempIndividualsAssociation;
329
        SpecimenOrObservationBase tempSpecimenOrObservationBase;
330
        List<IndividualsAssociation> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
331
        for (IndividualsAssociation element : elements) {
332
            tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
333
            if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
334
                tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
335
                if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
336
                    try {
337
                        derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
338
                    } catch (DerivedUnitFacadeNotSupportedException e) {
339
                        logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
340
                    }
341
                }
342
            }
343
        }
344

    
345
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
346

    
347
        return derivedUnitFacadeList;
348
    }
349

    
350

    
351
    @Override
352
    public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
353
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
354

    
355
        return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
356
    }
357

    
358
    @Override
359
    public Collection<SpecimenNodeWrapper> listUuidAndTitleCacheByAssociatedTaxon(List<UUID> taxonNodeUuids,
360
            Integer limit, Integer start) {
361
        return dao.listUuidAndTitleCacheByAssociatedTaxon(taxonNodeUuids, limit, start);
362
        }
363

    
364
    @Override
365
    public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
366
        return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
367
    }
368

    
369
    @Override
370
    public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
371
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
372
            List<String> propertyPaths) {
373

    
374
        if (!getSession().contains(associatedTaxon)) {
375
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
376
        }
377

    
378
        // gather the IDs of all relevant field units
379
        Set<UUID> fieldUnitUuids = new HashSet<>();
380
        List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
381
        for (SpecimenOrObservationBase<?> specimen : records) {
382
            for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid(), null)) {
383
                fieldUnitUuids.add(fieldUnit.getUuid());
384
            }
385
        }
386
        //dao.list() does the paging of the field units. Passing the field units directly to the Pager would not work
387
        List<SpecimenOrObservationBase> fieldUnits = dao.list(fieldUnitUuids, pageSize, pageNumber, orderHints, propertyPaths);
388
        return new DefaultPagerImpl<>(pageNumber, fieldUnitUuids.size(), pageSize, fieldUnits);
389
    }
390

    
391
    @Override
392
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
393

    
394
        if (!getSession().contains(fieldUnit)) {
395
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
396
        }
397
        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
398

    
399
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
400

    
401
        if (fieldUnit.getGatheringEvent() != null) {
402
            GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
403
            // Country
404
            NamedArea country = gatheringEvent.getCountry();
405
            fieldUnitDTO.setCountry(country != null ? country.getLabel() : null);
406
            // Collection
407
            AgentBase collector = gatheringEvent.getCollector();
408
            String fieldNumber = fieldUnit.getFieldNumber();
409
            String collectionString = "";
410
            if (collector != null || fieldNumber != null) {
411
                collectionString += collector != null ? collector : "";
412
                if (!collectionString.isEmpty()) {
413
                    collectionString += " ";
414
                }
415
                collectionString += (fieldNumber != null ? fieldNumber : "");
416
                collectionString.trim();
417
            }
418
            fieldUnitDTO.setCollection(collectionString);
419
            // Date
420
            Partial gatheringDate = gatheringEvent.getGatheringDate();
421
            String dateString = null;
422
            if (gatheringDate != null) {
423
                dateString = gatheringDate.toString();
424
            }
425
            else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
426
                dateString = gatheringEvent.getTimeperiod().getFreeText();
427
            }
428
            fieldUnitDTO.setDate(dateString);
429
        }
430

    
431
        // Taxon Name
432
        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
433

    
434
        // Herbaria map
435
        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<>();
436
        // List of accession numbers for citation
437
        List<String> preservedSpecimenAccessionNumbers = new ArrayList<>();
438

    
439
        // assemble preserved specimen DTOs
440
        Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
441
        for (DerivationEvent derivationEvent : derivationEvents) {
442
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
443
            for (DerivedUnit derivedUnit : derivatives) {
444
                if(!derivedUnit.isPublish()){
445
                    continue;
446
                }
447
                // collect accession numbers for citation
448
                String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
449
                if (mostSignificantIdentifier != null) {
450
                    preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
451
                }
452
                // collect collections for herbaria column
453
                if (derivedUnit.getCollection() != null) {
454
                    Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
455
                    if (herbariumCount == null) {
456
                        herbariumCount = 0;
457
                    }
458
                    collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
459
                }
460
                if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
461
                    PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
462
                    fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
463
                    fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
464
                    fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
465
                    fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
466
                    fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
467
                }
468
            }
469
        }
470
        // assemble derivate data DTO
471
        assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
472

    
473
        // assemble citation
474
        String citation = fieldUnit.getTitleCache();
475
        if((CdmUtils.isBlank(citation) || citation.equals(IdentifiableEntityDefaultCacheStrategy.TITLE_CACHE_GENERATION_NOT_IMPLEMENTED))
476
                && !fieldUnit.isProtectedTitleCache()){
477
            fieldUnit.setTitleCache(null);
478
            citation = fieldUnit.getTitleCache();
479
        }
480
        if (!preservedSpecimenAccessionNumbers.isEmpty()) {
481
            citation += " (";
482
            for (String accessionNumber : preservedSpecimenAccessionNumbers) {
483
                if (!accessionNumber.isEmpty()) {
484
                    citation += accessionNumber + SEPARATOR_STRING;
485
                }
486
            }
487
            citation = removeTail(citation, SEPARATOR_STRING);
488
            citation += ")";
489
        }
490
        fieldUnitDTO.setCitation(citation);
491

    
492
        // assemble herbaria string
493
        String herbariaString = "";
494
        for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
495
            eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
496
            if (collection.getCode() != null) {
497
                herbariaString += collection.getCode();
498
            }
499
            if (e.getValue() > 1) {
500
                herbariaString += "(" + e.getValue() + ")";
501
            }
502
            herbariaString += SEPARATOR_STRING;
503
        }
504
        herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
505
        fieldUnitDTO.setHerbarium(herbariaString);
506

    
507
        return fieldUnitDTO;
508
    }
509

    
510
    @Override
511
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
512
        return assemblePreservedSpecimenDTO(derivedUnit, null);
513
    }
514

    
515
    @Override
516
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
517
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
518
            return derivedUnit.getAccessionNumber();
519
        }
520
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
521
            return derivedUnit.getBarcode();
522
        }
523
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
524
            return derivedUnit.getCatalogNumber();
525
        }
526
        return null;
527
    }
528

    
529
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
530
        if (!getSession().contains(derivedUnit)) {
531
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
532
        }
533
        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
534

    
535
        //specimen identifier
536
        FormatKey collectionKey = FormatKey.COLLECTION_CODE;
537
        String specimenIdentifier = CdmFormatterFactory.format(derivedUnit, collectionKey);
538
        if (CdmUtils.isBlank(specimenIdentifier)) {
539
            collectionKey = FormatKey.COLLECTION_NAME;
540
        }
541
        specimenIdentifier = CdmFormatterFactory.format(derivedUnit, new FormatKey[] {
542
                collectionKey, FormatKey.SPACE,
543
                FormatKey.MOST_SIGNIFICANT_IDENTIFIER, FormatKey.SPACE });
544
        if(CdmUtils.isBlank(specimenIdentifier)){
545
            specimenIdentifier = derivedUnit.getUuid().toString();
546
        }
547
        preservedSpecimenDTO.setAccessionNumber(specimenIdentifier);
548
        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
549

    
550
        //preferred stable URI
551
        preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
552

    
553
        // citation
554
        Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit, null);
555
        if (fieldUnits.size() == 1) {
556
            preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
557
        }
558
        else{
559
            preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
560
        }
561

    
562
        // character state data
563
        Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
564
        if (!characterDataForSpecimen.isEmpty()) {
565
            if (fieldUnitDTO != null) {
566
                fieldUnitDTO.setHasCharacterData(true);
567
            }
568
        }
569
        for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
570
            String character = descriptionElementBase.getFeature().getLabel();
571
            ArrayList<Language> languages = new ArrayList<>(Collections.singleton(Language.DEFAULT()));
572
            if (descriptionElementBase instanceof QuantitativeData) {
573
                QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
574
                DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
575
                String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
576
                preservedSpecimenDTO.addCharacterData(character, state);
577
            }
578
            else if(descriptionElementBase instanceof CategoricalData){
579
                CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
580
                DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
581
                String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
582
                preservedSpecimenDTO.addCharacterData(character, state);
583
            }
584
        }
585
        // check type designations
586
        Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
587
        for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
588
            if (fieldUnitDTO != null) {
589
                fieldUnitDTO.setHasType(true);
590
            }
591
            TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
592
            if (typeStatus != null) {
593
                List<String> typedTaxaNames = new ArrayList<>();
594
                String label = typeStatus.getLabel();
595
                Set<TaxonName> typifiedNames = specimenTypeDesignation.getTypifiedNames();
596
                for (TaxonName taxonName : typifiedNames) {
597
                    typedTaxaNames.add(taxonName.getFullTitleCache());
598
                }
599
                preservedSpecimenDTO.addTypes(label, typedTaxaNames);
600
            }
601
        }
602

    
603
        // individuals associations
604
        Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
605
        for (IndividualsAssociation individualsAssociation : individualsAssociations) {
606
            if (individualsAssociation.getInDescription() != null) {
607
                if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
608
                    TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
609
                    Taxon taxon = taxonDescription.getTaxon();
610
                    if (taxon != null) {
611
                        preservedSpecimenDTO.addAssociatedTaxon(taxon);
612
                    }
613
                }
614
            }
615
        }
616
        // assemble sub derivates
617
        preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
618
        return preservedSpecimenDTO;
619
    }
620

    
621
    private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
622
        DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
623
        Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
624
        for (DerivedUnit childDerivate : childDerivates) {
625
            // assemble molecular data
626
            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
627
            if (childDerivate.isInstanceOf(DnaSample.class)) {
628
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
629
                    // TODO implement TissueSample assembly for web service
630
                }
631
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
632

    
633
                    DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
634
                    if (!dna.getSequences().isEmpty()) {
635
                        derivateDTO.setHasDna(true);
636
                    }
637
                    for (Sequence sequence : dna.getSequences()) {
638
                        URI boldUri = null;
639
                        try {
640
                            boldUri = sequence.getBoldUri();
641
                        } catch (URISyntaxException e1) {
642
                            logger.error("Could not create BOLD URI", e1);
643
                        }
644
                        final DefinedTerm dnaMarker = sequence.getDnaMarker();
645
                        Link providerLink = null;
646
                        if(boldUri!=null && dnaMarker!=null){
647
                        	providerLink = new DerivateDataDTO.Link(boldUri, dnaMarker.getLabel());
648
                        }
649
                        MolecularData molecularData = derivateDataDTO.addProviderLink(providerLink);
650

    
651
                        //contig file
652
                        ContigFile contigFile = null;
653
                        if (sequence.getContigFile() != null) {
654
                            MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
655
                            if (contigMediaRepresentationPart != null) {
656
                                contigFile = molecularData.addContigFile(new Link(contigMediaRepresentationPart.getUri(), "contig"));
657
                            }
658
                        }
659
                        else{
660
                        	contigFile = molecularData.addContigFile(null);
661
                        }
662
                        // primer files
663
                        if (sequence.getSingleReads() != null) {
664
                            int readCount = 1;
665
                            for (SingleRead singleRead : sequence.getSingleReads()) {
666
                                MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
667
                                if (pherogramMediaRepresentationPart != null) {
668
                                    contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
669
                                }
670
                            }
671
                        }
672
                    }
673
                }
674
            }
675
            // assemble media data
676
            else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
677
                MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
678

    
679
                URI mediaUri = getMediaUri(media);
680
                if (media.getKindOfUnit() != null) {
681
                    // specimen scan
682
                    if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidSpecimenScan)) {
683
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
684
                        derivateDTO.setHasSpecimenScan(true);
685
                        String imageLinkText = "scan";
686
                        if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
687
                            imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
688
                        }
689
                        derivateDataDTO.addSpecimenScan(mediaUri, imageLinkText);
690
                    }
691
                    // detail image
692
                    else if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidDetailImage)) {
693
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
694
                        derivateDTO.setHasDetailImage(true);
695
                        String motif = "detail image";
696
                        if (media.getMediaSpecimen()!=null){
697
                        	if(CdmUtils.isNotBlank(media.getMediaSpecimen().getTitleCache())) {
698
                        		motif = media.getMediaSpecimen().getTitleCache();
699
                        	}
700
                        }
701
                        derivateDataDTO.addDetailImage(mediaUri, motif);
702
                    }
703
                }
704
            }
705
        }
706
        return derivateDataDTO;
707
    }
708

    
709
    private String removeTail(String string, final String tail) {
710
        if (string.endsWith(tail)) {
711
            string = string.substring(0, string.length() - tail.length());
712
        }
713
        return string;
714
    }
715

    
716
    private URI getMediaUri(MediaSpecimen mediaSpecimen) {
717
        URI mediaUri = null;
718
        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
719
        if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
720
            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
721
            if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
722
                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
723
                if (part.getUri() != null) {
724
                    mediaUri = part.getUri();
725
                }
726
            }
727
        }
728
        return mediaUri;
729
    }
730

    
731
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
732
        Collection<DerivedUnit> derivedUnits = new ArrayList<>();
733
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
734
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
735
                derivedUnits.add(derivative);
736
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
737
            }
738
        }
739
        return derivedUnits;
740
    }
741

    
742

    
743
    @SuppressWarnings("unchecked")
744
    @Override
745
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includedRelationships,
746
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
747

    
748
        Set<Taxon> taxa = new HashSet<>();
749
        Set<Integer> occurrenceIds = new HashSet<>();
750
        List<T> occurrences = new ArrayList<>();
751
        boolean includeUnpublished = INCLUDE_UNPUBLISHED;
752

    
753
        // Integer limit = PagerUtils.limitFor(pageSize);
754
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
755

    
756
        if (!getSession().contains(associatedTaxon)) {
757
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
758
        }
759

    
760
        if (includedRelationships != null) {
761
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includedRelationships, maxDepth, includeUnpublished, null, null, propertyPaths);
762
        }
763

    
764
        taxa.add(associatedTaxon);
765

    
766
        for (Taxon taxon : taxa) {
767
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
768
            for (SpecimenOrObservationBase<?> o : perTaxonOccurrences) {
769
                occurrenceIds.add(o.getId());
770
            }
771
        }
772
        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
773

    
774
        return new DefaultPagerImpl<T>(pageNumber, Long.valueOf(occurrenceIds.size()), pageSize, occurrences);
775

    
776
    }
777

    
778
    @Override
779
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
780
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
781

    
782
        UUID uuid = UUID.fromString(taxonUUID);
783
        Taxon tax = (Taxon) taxonService.load(uuid);
784
        // TODO REMOVE NULL STATEMENT
785
        type = null;
786
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
787

    
788
    }
789

    
790
    @Override
791
    public  List<DerivedUnit> findByAccessionNumber(
792
            String accessionNumberString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
793
            List<String> propertyPaths)  {
794

    
795
        List<DerivedUnit> records = new ArrayList<>();
796
        records = dao.getByGeneticAccessionNumber(accessionNumberString, propertyPaths);
797

    
798
        return records;
799

    
800
    }
801

    
802
    @Override
803
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
804
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
805
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
806
            List<String> propertyPaths) throws IOException, LuceneParseException {
807

    
808
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
809

    
810
        // --- execute search
811
        TopGroups<BytesRef> topDocsResultSet;
812
        try {
813
            topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
814
        } catch (ParseException e) {
815
            LuceneParseException parseException = new LuceneParseException(e.getMessage());
816
            parseException.setStackTrace(e.getStackTrace());
817
            throw parseException;
818
        }
819

    
820
        Map<CdmBaseType, String> idFieldMap = new HashMap<>();
821
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
822

    
823
        // --- initialize taxa, highlight matches ....
824
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
825
        @SuppressWarnings("rawtypes")
826
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
827
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
828

    
829
        int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
830

    
831
        return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
832

    
833
    }
834

    
835
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
836
            List<Language> languages, boolean highlightFragments) {
837

    
838
        Builder finalQueryBuilder = new Builder();
839
        Builder textQueryBuilder = new Builder();
840

    
841
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
842
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
843

    
844
        // --- criteria
845
        luceneSearch.setCdmTypRestriction(clazz);
846
        if (queryString != null) {
847
            textQueryBuilder.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
848
            finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
849
        }
850

    
851
        // --- spacial query
852
        if (bbox != null) {
853
            finalQueryBuilder.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
854
        }
855

    
856
        luceneSearch.setQuery(finalQueryBuilder.build());
857

    
858
        // --- sorting
859
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false) };
860
        luceneSearch.setSortFields(sortFields);
861

    
862
        if (highlightFragments) {
863
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
864
        }
865
        return luceneSearch;
866
    }
867

    
868

    
869
    @Override
870
    public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid, List<String> propertyPaths) {
871
        //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
872
        //from which this DerivedUnit was derived until all FieldUnits are found.
873

    
874
        // FIXME: use HQL queries to increase performance
875
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid, propertyPaths);
876
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
877
        Collection<FieldUnit> fieldUnits = new ArrayList<>();
878

    
879
        if (specimen.isInstanceOf(FieldUnit.class)) {
880
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
881
        }
882
        else if(specimen.isInstanceOf(DerivedUnit.class)){
883
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), propertyPaths));
884
        }
885
        return fieldUnits;
886
    }
887

    
888
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit, List<String> propertyPaths) {
889
        Collection<FieldUnit> fieldUnits = new HashSet<>();
890
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
891
        if (originals != null && !originals.isEmpty()) {
892
            for (SpecimenOrObservationBase<?> original : originals) {
893
                if (original.isInstanceOf(FieldUnit.class)) {
894
                    fieldUnits.add((FieldUnit) load(original.getUuid(), propertyPaths));
895
                }
896
                else if(original.isInstanceOf(DerivedUnit.class)){
897
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class), propertyPaths));
898
                }
899
            }
900
        }
901
        return fieldUnits;
902
    }
903

    
904
    @Override
905
    @Transactional(readOnly = false)
906
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
907
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
908
    }
909

    
910
    @Override
911
    @Transactional(readOnly = false)
912
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
913
        // reload specimens to avoid session conflicts
914
        DnaSample from = (DnaSample) load(fromUuid);
915
        DnaSample to = (DnaSample) load(toUuid);
916
        Sequence sequence = sequenceService.load(sequenceUuid);
917

    
918
        if (from == null || to == null || sequence == null) {
919
            throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +
920
                    "Operation was move "+sequence+ " from "+from+" to "+to);
921
        }
922
        UpdateResult result = new UpdateResult();
923
        from.removeSequence(sequence);
924
        saveOrUpdate(from);
925
        to.addSequence(sequence);
926
        saveOrUpdate(to);
927
        result.setStatus(Status.OK);
928
        result.addUpdatedObject(from);
929
        result.addUpdatedObject(to);
930
        return result;
931
    }
932

    
933
    @Override
934
    @Transactional(readOnly = false)
935
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
936
        return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
937
    }
938

    
939
    @Override
940
    @Transactional(readOnly = false)
941
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
942
        // reload specimens to avoid session conflicts
943
        SpecimenOrObservationBase<?> from = null;
944
        if(specimenFromUuid!=null){
945
            from = load(specimenFromUuid);
946
        }
947
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
948
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
949

    
950
        if ((specimenFromUuid!=null && from == null) || to == null || derivate == null) {
951
            throw new TransientObjectException("One of the CDM entities has not been saved to the data base yet. Moving only works for persisted/saved CDM entities.\n" +
952
                    "Operation was move "+derivate+ " from "+from+" to "+to);
953
        }
954
        UpdateResult result = new UpdateResult();
955
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
956
        SpecimenOrObservationType toType = to.getRecordBasis();
957
        // check if type is a sub derivate type
958
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
959
                || derivateType==SpecimenOrObservationType.Media //moving media always works
960
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
961
            if(from!=null){
962
                // remove derivation event from parent specimen of dragged object
963
                DerivationEvent eventToRemove = null;
964
                for (DerivationEvent event : from.getDerivationEvents()) {
965
                    if (event.getDerivatives().contains(derivate)) {
966
                        eventToRemove = event;
967
                        break;
968
                    }
969
                }
970
                from.removeDerivationEvent(eventToRemove);
971
                if(eventToRemove!=null){
972
                    // add new derivation event to target and copy the event parameters of the old one
973
                    DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
974
                    derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
975
                    derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
976
                    derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
977
                    derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
978
                    derivedFromNewOriginalEvent.setType(eventToRemove.getType());
979
                    to.addDerivationEvent(derivedFromNewOriginalEvent);
980
                    derivate.setDerivedFrom(derivedFromNewOriginalEvent);
981
                }
982
            }
983
            else{
984
                //derivative had no parent before so we use empty derivation event
985
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
986
                to.addDerivationEvent(derivedFromNewOriginalEvent);
987
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
988
            }
989

    
990
            if(from!=null){
991
                saveOrUpdate(from);
992
            }
993
            saveOrUpdate(to);
994
            result.setStatus(Status.OK);
995
            result.addUpdatedObject(from);
996
            result.addUpdatedObject(to);
997
        } else {
998
            result.setStatus(Status.ERROR);
999
        }
1000
        return result;
1001
    }
1002

    
1003
    @Override
1004
    public DeleteResult isDeletable(UUID specimenUuid, DeleteConfiguratorBase config) {
1005
        DeleteResult deleteResult = new DeleteResult();
1006
        SpecimenOrObservationBase specimen = this.load(specimenUuid);
1007
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1008

    
1009
        // check elements found by super method
1010
        Set<CdmBase> relatedObjects = super.isDeletable(specimenUuid, config).getRelatedObjects();
1011
        for (CdmBase cdmBase : relatedObjects) {
1012
            // check for type designation
1013
            if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
1014
                deleteResult.setAbort();
1015
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is a type specimen."));
1016
                deleteResult.addRelatedObject(cdmBase);
1017
                break;
1018
            }
1019
            // check for IndividualsAssociations
1020
            else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
1021
                deleteResult.setAbort();
1022
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still associated via IndividualsAssociations"));
1023
                deleteResult.addRelatedObject(cdmBase);
1024
                break;
1025
            }
1026
            // check for taxon description
1027
            else if(cdmBase.isInstanceOf(TaxonDescription.class)
1028
                    && HibernateProxyHelper.deproxy(cdmBase, TaxonDescription.class).getDescribedSpecimenOrObservation().equals(specimen)
1029
                    && !specimenDeleteConfigurator.isDeleteFromDescription()){
1030
                deleteResult.setAbort();
1031
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still used as \"Described Specimen\" in a taxon description."));
1032
                deleteResult.addRelatedObject(cdmBase);
1033
                break;
1034
            }
1035
            // check for children and parents (derivation events)
1036
            else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1037
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1038
                // check if derivation event is empty
1039
                if (!derivationEvent.getDerivatives().isEmpty() && derivationEvent.getOriginals().contains(specimen)) {
1040
                    // if derivationEvent is the childEvent and contains derivations
1041
//                    if (derivationEvent.getDerivatives().contains(specimen)) {
1042
//                        //if it is the parent event the specimen is still deletable
1043
//                        continue;
1044
//                    }
1045
                    if(!specimenDeleteConfigurator.isDeleteChildren()){
1046
                        //if children should not be deleted then it is undeletable
1047
                        deleteResult.setAbort();
1048
                        deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation still has child derivatives."));
1049
                        deleteResult.addRelatedObject(cdmBase);
1050
                        break;
1051
                    }
1052
                    else{
1053
                        // check all children if they can be deleted
1054
                        Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1055
                        DeleteResult childResult = new DeleteResult();
1056
                        for (DerivedUnit derivedUnit : derivatives) {
1057
                            childResult.includeResult(isDeletable(derivedUnit.getUuid(), specimenDeleteConfigurator));
1058
                        }
1059
                        if (!childResult.isOk()) {
1060
                            deleteResult.setAbort();
1061
                            deleteResult.includeResult(childResult);
1062
                            deleteResult.addRelatedObject(cdmBase);
1063
                            break;
1064
                        }
1065
                    }
1066
                }
1067
            }
1068
            // check for amplification
1069
            else if (cdmBase.isInstanceOf(AmplificationResult.class)
1070
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1071
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1072
                deleteResult.setAbort();
1073
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1074
                deleteResult.addRelatedObject(cdmBase);
1075
                break;
1076
            }
1077
            // check for sequence
1078
            else if (cdmBase.isInstanceOf(Sequence.class)
1079
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1080
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1081
                deleteResult.setAbort();
1082
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1083
                deleteResult.addRelatedObject(cdmBase);
1084
                break;
1085
            }
1086
        }
1087
        if (deleteResult.isOk()) {
1088
            //add all related object if deletion is OK so they can be handled by the delete() method
1089
            deleteResult.addRelatedObjects(relatedObjects);
1090
        }
1091
        return deleteResult;
1092
    }
1093

    
1094
    /**
1095
     * {@inheritDoc}
1096
     */
1097
    @Transactional(readOnly = false)
1098
    @Override
1099
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1100
        return delete(load(specimenUuid), config);
1101
    }
1102

    
1103

    
1104
    @Transactional(readOnly = false)
1105
    @Override
1106
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1107
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1108

    
1109
        DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
1110
        if (!deleteResult.isOk()) {
1111
            return deleteResult;
1112
        }
1113

    
1114
        if (config.isDeleteChildren()) {
1115
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1116
            //clone to avoid concurrent modification
1117
            //can happen if the child is deleted and deleted its own derivedFrom event
1118
            Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
1119
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1120
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1121
                Iterator<DerivedUnit> it = derivatives.iterator();
1122
                Set<DerivedUnit> derivativesToDelete = new HashSet<>();
1123
                while (it.hasNext()) {
1124
                    DerivedUnit unit = it.next();
1125
                    derivativesToDelete.add(unit);
1126
                }
1127
                for (DerivedUnit unit:derivativesToDelete){
1128
                    deleteResult.includeResult(delete(unit, config));
1129
                }
1130
            }
1131
        }
1132

    
1133

    
1134

    
1135

    
1136
        // check related objects
1137
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1138

    
1139
        for (CdmBase relatedObject : relatedObjects) {
1140
            // check for TypeDesignations
1141
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1142
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1143
                designation.setTypeSpecimen(null);
1144
                List<TaxonName> typifiedNames = new ArrayList<>();
1145
                typifiedNames.addAll(designation.getTypifiedNames());
1146
                for (TaxonName taxonName : typifiedNames) {
1147
                    taxonName.removeTypeDesignation(designation);
1148
                }
1149
            }
1150
            // delete IndividualsAssociation
1151
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1152
                IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1153
                association.setAssociatedSpecimenOrObservation(null);
1154
                association.getInDescription().removeElement(association);
1155
            }
1156
            // check for "described specimen" (deprecated)
1157
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1158
                TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1159
                description.setDescribedSpecimenOrObservation(null);
1160
            }
1161
            // check for specimen description
1162
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1163
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1164
                specimenDescription.setDescribedSpecimenOrObservation(null);
1165
                // check if description is a description of the given specimen
1166
                if (specimen.getDescriptions().contains(specimenDescription)) {
1167
                    specimen.removeDescription(specimenDescription);
1168
                }
1169
                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription.getUuid(), null);
1170
                if (descriptionDelete.isOk()){
1171
                    deleteResult.includeResult(descriptionService.delete(specimenDescription));
1172
                }
1173
            }
1174
            // check for amplification
1175
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1176
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1177
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1178
            }
1179
            // check for sequence
1180
            if (relatedObject.isInstanceOf(Sequence.class)) {
1181
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1182
                sequence.getDnaSample().removeSequence(sequence);
1183
            }
1184
            // check for children and parents (derivation events)
1185
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1186
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1187
                // parent derivation event (derivedFrom)
1188
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1189
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1190
                    if (derivationEvent.getDerivatives().isEmpty()) {
1191
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1192
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1193
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1194
                            deleteResult.addUpdatedObject(specimenOrObservationBase);
1195
                        }
1196
                        // if derivationEvent has no derivates anymore, delete it
1197
                        deleteResult.includeResult(eventService.delete(derivationEvent));
1198
                    }
1199
                }
1200
                else{
1201
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1202
                }
1203
            }
1204
        }
1205
        if (specimen instanceof FieldUnit){
1206
            FieldUnit fieldUnit = HibernateProxyHelper.deproxy(specimen, FieldUnit.class);
1207
            GatheringEvent event = fieldUnit.getGatheringEvent();
1208
            fieldUnit.setGatheringEvent(null);
1209
            if (event != null){
1210
                DeleteResult result = eventService.isDeletable(event.getUuid(), null);
1211
                if (result.isOk()){
1212
                    deleteResult.includeResult( eventService.delete(event));
1213
                }
1214
            }
1215

    
1216
        }
1217
        deleteResult.includeResult(delete(specimen));
1218

    
1219
        return deleteResult;
1220
    }
1221

    
1222
    @Override
1223
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1224
        return dao.listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths);
1225
    }
1226

    
1227
    /**
1228
     * {@inheritDoc}
1229
     */
1230
    @Override
1231
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1232
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1233
        return listAssociatedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1234
    }
1235
    @Override
1236
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
1237
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1238
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1239

    
1240
        //individuals associations
1241
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
1242
        //type designation
1243
        if(specimen.isInstanceOf(DerivedUnit.class)){
1244
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class),
1245
                  includeUnpublished, limit, start, orderHints, propertyPaths));
1246
        }
1247
        //determinations
1248
        associatedTaxa.addAll(listDeterminedTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
1249

    
1250
        return associatedTaxa;
1251
    }
1252

    
1253

    
1254

    
1255
    /**
1256
     * {@inheritDoc}
1257
     */
1258
    @Override
1259
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1260
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1261
        return listDeterminedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1262
    }
1263
    @Override
1264
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished, Integer limit, Integer start,
1265
            List<OrderHint> orderHints, List<String> propertyPaths) {
1266
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1267
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1268
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1269
                if(determinationEvent.getTaxon()!=null){
1270
                    associatedTaxa.add(taxonService.load(determinationEvent.getTaxon().getUuid(), includeUnpublished, propertyPaths));
1271
                }
1272
                if(determinationEvent.getTaxonName()!=null){
1273
                    Collection<TaxonBase> taxonBases = determinationEvent.getTaxonName().getTaxonBases();
1274
                    for (TaxonBase taxonBase : taxonBases) {
1275
                        associatedTaxa.add(taxonService.load(taxonBase.getUuid(), includeUnpublished, propertyPaths));
1276
                    }
1277
                }
1278
            }
1279
        }
1280
        return associatedTaxa;
1281
    }
1282

    
1283
    /**
1284
     * {@inheritDoc}
1285
     */
1286
    @Override
1287
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1288
            List<OrderHint> orderHints, List<String> propertyPaths) {
1289
        return listTypeDesignationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1290
    }
1291
    @Override
1292
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, boolean includeUnpublished,
1293
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1294
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1295
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1296
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1297
                Set<TaxonName> typifiedNames = typeDesignation.getTypifiedNames();
1298
                for (TaxonName taxonName : typifiedNames) {
1299
                    Set<Taxon> taxa = taxonName.getTaxa();
1300
                    for (Taxon taxon : taxa) {
1301
                        associatedTaxa.add(taxonService.load(taxon.getUuid(), includeUnpublished, propertyPaths));
1302
                    }
1303
                }
1304
            }
1305
        }
1306
        return associatedTaxa;
1307
    }
1308

    
1309
    /**
1310
     * {@inheritDoc}
1311
     */
1312
    @Override
1313
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1314
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1315
        return listIndividualsAssociationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1316
    }
1317

    
1318
    @Override
1319
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
1320
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1321
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1322
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, null, null, null, null)) {
1323
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1324
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1325
                if(taxonDescription.getTaxon()!=null){
1326
                    associatedTaxa.add(taxonService.load(taxonDescription.getTaxon().getUuid(), includeUnpublished, propertyPaths));
1327
                }
1328
            }
1329
        }
1330
        return associatedTaxa;
1331
    }
1332

    
1333
    @Override
1334
    public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1335
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1336
        return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1337
    }
1338

    
1339
    @Override
1340
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1341
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1342
            List<OrderHint> orderHints, List<String> propertyPaths) {
1343
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
1344
        for (DerivedUnit specimen : specimens) {
1345
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1346
            typeDesignationMap.put(specimen, typeDesignations);
1347
        }
1348
        return typeDesignationMap;
1349
    }
1350

    
1351
    @Override
1352
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1353
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1354
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1355
    }
1356

    
1357
    @Override
1358
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1359
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1360
            List<String> propertyPaths) {
1361
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1362
    }
1363

    
1364
    @Override
1365
    @Deprecated //this is not a service layer task so it may be removed in future versions
1366
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1367
        if (specimen != null) {
1368
            return specimen.characterData();
1369
        }else{
1370
            return new ArrayList<>();
1371
        }
1372
    }
1373

    
1374
    @Override
1375
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1376
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1377
        if (specimen != null) {
1378
            return getCharacterDataForSpecimen(specimen);
1379
        }
1380
        else{
1381
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1382
        }
1383
    }
1384

    
1385

    
1386
    @Override
1387
    public long countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1388
        if (config instanceof FindOccurrencesConfigurator) {
1389
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1390
            Taxon taxon = null;
1391
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1392
                TaxonBase<?> taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1393
                if(taxonBase.isInstanceOf(Taxon.class)){
1394
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1395
                }
1396
            }
1397
            TaxonName taxonName = null;
1398
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1399
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1400
            }
1401
            /*TODO: #6484 Neither isRetrieveIndirectlyAssociatedSpecimens() nor the AssignmentStatus
1402
             * is currently reflected in the HQL query. So using these in the count method will
1403
             * significantly slow down this method as we have to retreive the entities instead of
1404
             * the just the amount.
1405
             */
1406
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig.getAssignmentStatus().equals(AssignmentStatus.ALL_SPECIMENS)){
1407
                List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1408
                occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1409
                        occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1410
                        occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1411
                        occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1412
                occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1413
                return occurrences.size();
1414
            }
1415

    
1416
            return dao.countOccurrences(occurrenceConfig.getClazz(),
1417
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1418
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1419
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1420
        }
1421
        else{
1422
            return dao.countByTitle(config.getTitleSearchString());
1423
        }
1424
    }
1425

    
1426
    @Override
1427
    public Pager<UuidAndTitleCache<SpecimenOrObservationBase>> findByTitleUuidAndTitleCache(
1428
            FindOccurrencesConfigurator config){
1429
        List<UuidAndTitleCache<SpecimenOrObservationBase>> occurrences = new ArrayList<>();
1430
        Taxon taxon = null;
1431
        if(config.getAssociatedTaxonUuid()!=null){
1432
            TaxonBase<?> taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
1433
            if(taxonBase.isInstanceOf(Taxon.class)){
1434
                taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1435
            }
1436
        }
1437
        TaxonName taxonName = null;
1438
        if(config.getAssociatedTaxonNameUuid()!=null){
1439
            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
1440
        }
1441
        occurrences.addAll(dao.findOccurrencesUuidAndTitleCache(config.getClazz(),
1442
                config.getTitleSearchString(), config.getSignificantIdentifier(),
1443
                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
1444
                config.getOrderHints()));
1445

    
1446
        return new DefaultPagerImpl<UuidAndTitleCache<SpecimenOrObservationBase>>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1447
    }
1448

    
1449
    @Override
1450
    public Pager<SpecimenOrObservationBase> findByTitle(
1451
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1452
        if (config instanceof FindOccurrencesConfigurator) {
1453
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1454
            List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1455
            Taxon taxon = null;
1456
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1457
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1458
                if(taxonBase.isInstanceOf(Taxon.class)){
1459
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1460
                }
1461
            }
1462
            TaxonName taxonName = null;
1463
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1464
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1465
            }
1466
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1467
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1468
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1469
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1470
            occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1471

    
1472
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1473
        }
1474
        return super.findByTitle(config);
1475
    }
1476

    
1477
    private List<SpecimenOrObservationBase> filterOccurencesByAssignmentAndHierarchy(
1478
            FindOccurrencesConfigurator occurrenceConfig, List<SpecimenOrObservationBase> occurrences, Taxon taxon,
1479
            TaxonName taxonName) {
1480
        //filter out (un-)assigned specimens
1481
        if(taxon==null && taxonName==null){
1482
            AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1483
            List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
1484
            if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1485
                for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1486
                    boolean includeUnpublished = true;  //TODO not sure if this is correct, maybe we have to propagate publish flag to higher methods.
1487
                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase,
1488
                            includeUnpublished, null, null, null, null);
1489
                    if(!associatedTaxa.isEmpty()){
1490
                        specimenWithAssociations.add(specimenOrObservationBase);
1491
                    }
1492
                }
1493
            }
1494
            if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1495
                occurrences.removeAll(specimenWithAssociations);
1496
            }
1497
            if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1498
                occurrences = new ArrayList<>(specimenWithAssociations);
1499
            }
1500
        }
1501
        // indirectly associated specimens
1502
        if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1503
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
1504
            for (SpecimenOrObservationBase specimen : occurrences) {
1505
                List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1506
                for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1507
                    if(!occurrences.contains(specimenOrObservationBase)){
1508
                        indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1509
                    }
1510
                }
1511
            }
1512
            occurrences = indirectlyAssociatedOccurrences;
1513
        }
1514
        return occurrences;
1515
    }
1516

    
1517
    @Override
1518
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1519
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
1520
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid(), null);
1521
        if(fieldUnits.isEmpty()){
1522
            allHierarchyDerivatives.add(specimen);
1523
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1524
        }
1525
        else{
1526
            for (FieldUnit fieldUnit : fieldUnits) {
1527
                allHierarchyDerivatives.add(fieldUnit);
1528
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1529
            }
1530
        }
1531
        return allHierarchyDerivatives;
1532
    }
1533

    
1534
    @Override
1535
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1536
        return getAllChildDerivatives(load(specimenUuid));
1537
    }
1538

    
1539
    @Override
1540
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1541
        if (specimen == null){
1542
            return null;
1543
        }
1544
        List<DerivedUnit> childDerivate = new ArrayList<>();
1545
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1546
        for (DerivationEvent derivationEvent : derivationEvents) {
1547
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1548
            for (DerivedUnit derivedUnit : derivatives) {
1549
                childDerivate.add(derivedUnit);
1550
                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1551
            }
1552
        }
1553
        return childDerivate;
1554
    }
1555

    
1556
    @Override
1557
    public long countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1558
        return countByTitle(config);
1559
    }
1560

    
1561
    /**
1562
     * {@inheritDoc}
1563
     */
1564
    @Override
1565
    public List<FieldUnit> getFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
1566
        return dao.getFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
1567
    }
1568
}
(83-83/106)