Project

General

Profile

Download (91.4 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.DNASampleDTO;
52
import eu.etaxonomy.cdm.api.service.dto.SpecimenOrObservationBaseDTO;
53
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO;
54
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.ContigFile;
55
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.Link;
56
import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.MolecularData;
57
import eu.etaxonomy.cdm.api.service.dto.FieldUnitDTO;
58
import eu.etaxonomy.cdm.api.service.dto.DerivedUnitDTO;
59
import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
60
import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
61
import eu.etaxonomy.cdm.api.service.pager.Pager;
62
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
63
import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
64
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
65
import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
66
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
67
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
68
import eu.etaxonomy.cdm.api.service.search.SearchResult;
69
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
70
import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
71
import eu.etaxonomy.cdm.common.CdmUtils;
72
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
73
import eu.etaxonomy.cdm.format.CdmFormatterFactory;
74
import eu.etaxonomy.cdm.format.ICdmFormatter.FormatKey;
75
import eu.etaxonomy.cdm.format.description.DefaultCategoricalDescriptionBuilder;
76
import eu.etaxonomy.cdm.format.description.DefaultQuantitativeDescriptionBuilder;
77
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
78
import eu.etaxonomy.cdm.model.CdmBaseType;
79
import eu.etaxonomy.cdm.model.agent.AgentBase;
80
import eu.etaxonomy.cdm.model.common.CdmBase;
81
import eu.etaxonomy.cdm.model.common.Language;
82
import eu.etaxonomy.cdm.model.description.CategoricalData;
83
import eu.etaxonomy.cdm.model.description.DescriptionBase;
84
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
85
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
86
import eu.etaxonomy.cdm.model.description.QuantitativeData;
87
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
88
import eu.etaxonomy.cdm.model.description.TaxonDescription;
89
import eu.etaxonomy.cdm.model.location.Country;
90
import eu.etaxonomy.cdm.model.location.NamedArea;
91
import eu.etaxonomy.cdm.model.location.Point;
92
import eu.etaxonomy.cdm.model.media.Media;
93
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
94
import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
95
import eu.etaxonomy.cdm.model.media.MediaUtils;
96
import eu.etaxonomy.cdm.model.molecular.AmplificationResult;
97
import eu.etaxonomy.cdm.model.molecular.DnaSample;
98
import eu.etaxonomy.cdm.model.molecular.Sequence;
99
import eu.etaxonomy.cdm.model.molecular.SingleRead;
100
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
101
import eu.etaxonomy.cdm.model.name.TaxonName;
102
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
103
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
104
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
105
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
106
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
107
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
108
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
109
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
110
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
111
import eu.etaxonomy.cdm.model.taxon.Taxon;
112
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
113
import eu.etaxonomy.cdm.model.term.DefinedTerm;
114
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
115
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
116
import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
117
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
118
import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
119
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
120
import eu.etaxonomy.cdm.persistence.query.AssignmentStatus;
121
import eu.etaxonomy.cdm.persistence.query.OrderHint;
122
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
123
import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
124

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

    
133
    static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
134

    
135
    @Autowired
136
    private IDefinedTermDao definedTermDao;
137

    
138
    @Autowired
139
    private IDescriptionService descriptionService;
140

    
141
    @Autowired
142
    private INameService nameService;
143

    
144
    @Autowired
145
    private IEventBaseService eventService;
146

    
147
    @Autowired
148
    private ITaxonService taxonService;
149

    
150
    @Autowired
151
    private ISequenceService sequenceService;
152

    
153
    @Autowired
154
    private AbstractBeanInitializer beanInitializer;
155

    
156
    @Autowired
157
    private ILuceneIndexToolProvider luceneIndexToolProvider;
158

    
159
    private static final String SEPARATOR_STRING = ", ";
160

    
161
    public OccurrenceServiceImpl() {
162
        logger.debug("Load OccurrenceService Bean");
163
    }
164

    
165

    
166
    @Override
167
    @Transactional(readOnly = false)
168
    public UpdateResult updateCaches(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
169
        if (clazz == null) {
170
            clazz = SpecimenOrObservationBase.class;
171
        }
172
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
173
    }
174

    
175
    /**
176
     * FIXME Candidate for harmonization
177
     * move to termService
178
     */
179
    @Override
180
    public Country getCountryByIso(String iso639) {
181
        return this.definedTermDao.getCountryByIso(iso639);
182

    
183
    }
184

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

    
199
    @Override
200
    @Autowired
201
    protected void setDao(IOccurrenceDao dao) {
202
        this.dao = dao;
203
    }
204

    
205
    @Override
206
    public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
207
        long numberOfResults = dao.countDerivationEvents(occurence);
208

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

    
214
        return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
215
    }
216

    
217
    @Override
218
    public long countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
219
        return dao.countDeterminations(occurence, taxonbase);
220
    }
221

    
222
    @Override
223
    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase,
224
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
225
        long numberOfResults = dao.countDeterminations(occurrence, taxonBase);
226

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

    
232
        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
233
    }
234

    
235
    @Override
236
    public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
237
        long numberOfResults = dao.countMedia(occurence);
238

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

    
244
        return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
245
    }
246

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

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

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

    
307
    @Override
308
    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache(Integer limit, String pattern) {
309
        return dao.getDerivedUnitUuidAndTitleCache(limit, pattern);
310
    }
311

    
312
    @Override
313
    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
314
        return dao.getFieldUnitUuidAndTitleCache();
315
    }
316

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

    
327
    @Override
328
    public List<DerivedUnitFacade> listDerivedUnitFacades(
329
            DescriptionBase description, List<String> derivedUnitFacadeInitStrategy) {
330

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

    
349
        beanInitializer.initializeAll(derivedUnitFacadeList, derivedUnitFacadeInitStrategy);
350

    
351
        return derivedUnitFacadeList;
352
    }
353

    
354

    
355
    @Override
356
    public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
357
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
358

    
359
        return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
360
    }
361

    
362
    @Override
363
    public Collection<SpecimenNodeWrapper> listUuidAndTitleCacheByAssociatedTaxon(List<UUID> taxonNodeUuids,
364
            Integer limit, Integer start) {
365
        return dao.listUuidAndTitleCacheByAssociatedTaxon(taxonNodeUuids, limit, start);
366
        }
367

    
368
    @Override
369
    public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
370
        return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
371
    }
372

    
373
    @Override
374
    public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
375
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
376
            List<String> propertyPaths) {
377

    
378
        if (!getSession().contains(associatedTaxon)) {
379
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
380
        }
381

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

    
395
    @Override
396
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit) {
397

    
398
        if (!getSession().contains(fieldUnit)) {
399
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
400
        }
401

    
402
        FieldUnitDTO fieldUnitDTO = FieldUnitDTO.fromEntity(fieldUnit);
403

    
404
        if (fieldUnit.getGatheringEvent() != null) {
405
            GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
406
            // Country
407
            NamedArea country = gatheringEvent.getCountry();
408
            fieldUnitDTO.setCountry(country != null ? country.getLabel() : null);
409
            // Collection
410
            AgentBase collector = gatheringEvent.getCollector();
411
            String fieldNumber = fieldUnit.getFieldNumber();
412
            String collectionString = "";
413
            if (collector != null || fieldNumber != null) {
414
                collectionString += collector != null ? collector : "";
415
                if (!collectionString.isEmpty()) {
416
                    collectionString += " ";
417
                }
418
                collectionString += (fieldNumber != null ? fieldNumber : "");
419
                collectionString.trim();
420
            }
421
            fieldUnitDTO.setCollectingString(collectionString);
422
            // Date
423
            Partial gatheringDate = gatheringEvent.getGatheringDate();
424
            String dateString = null;
425
            if (gatheringDate != null) {
426
                dateString = gatheringDate.toString();
427
            }
428
            else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
429
                dateString = gatheringEvent.getTimeperiod().getFreeText();
430
            }
431
            fieldUnitDTO.setDate(dateString);
432
        }
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 identifier = getMostSignificantIdentifier(derivedUnit);
449
                // collect collections for herbaria column
450
                eu.etaxonomy.cdm.model.occurrence.Collection collection = derivedUnit.getCollection();
451
                if (collection != null) {
452
                    //combine collection with identifier
453
                    if (identifier != null) {
454
                        if(collection.getCode()!=null){
455
                            identifier = (collection.getCode()!=null?collection.getCode():"[no collection]")+" "+identifier;
456
                        }
457
                        preservedSpecimenAccessionNumbers.add(identifier);
458
                    }
459

    
460
                    Integer herbariumCount = collectionToCountMap.get(collection);
461
                    if (herbariumCount == null) {
462
                        herbariumCount = 0;
463
                    }
464
                    collectionToCountMap.put(collection, herbariumCount + 1);
465
                }
466
                if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
467
                    DerivedUnitDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
468
                    fieldUnitDTO.addDerivate(preservedSpecimenDTO);
469
                    fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
470
                    fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
471
                    fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
472
                    fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
473
                }
474
            }
475
        }
476
        // assemble derivate data DTO
477
        assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
478

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

    
498
        // assemble herbaria string
499
        String herbariaString = "";
500
        for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
501
            eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
502
            if (collection.getCode() != null) {
503
                herbariaString += collection.getCode();
504
            }
505
            if (e.getValue() > 1) {
506
                herbariaString += "(" + e.getValue() + ")";
507
            }
508
            herbariaString += SEPARATOR_STRING;
509
        }
510
        herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
511
        fieldUnitDTO.setCollectionStatistics(herbariaString);
512

    
513
        return fieldUnitDTO;
514
    }
515

    
516
    @Override
517
    public DerivedUnitDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
518
        return assemblePreservedSpecimenDTO(derivedUnit, null);
519
    }
520

    
521
    @Override
522
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
523
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
524
            return derivedUnit.getAccessionNumber();
525
        }
526
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
527
            return derivedUnit.getBarcode();
528
        }
529
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
530
            return derivedUnit.getCatalogNumber();
531
        }
532
        return null;
533
    }
534

    
535
    public DerivedUnitDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
536
        if (!getSession().contains(derivedUnit)) {
537
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
538
        }
539
        DerivedUnitDTO preservedSpecimenDTO = new DerivedUnitDTO(derivedUnit);
540

    
541
        //specimen identifier
542
        FormatKey collectionKey = FormatKey.COLLECTION_CODE;
543
        String specimenIdentifier = CdmFormatterFactory.format(derivedUnit, collectionKey);
544
        if (CdmUtils.isBlank(specimenIdentifier)) {
545
            collectionKey = FormatKey.COLLECTION_NAME;
546
        }
547
        if(CdmUtils.isNotBlank(derivedUnit.getMostSignificantIdentifier())){
548
            specimenIdentifier = CdmFormatterFactory.format(derivedUnit, new FormatKey[] {
549
                    collectionKey, FormatKey.SPACE, FormatKey.OPEN_BRACKET,
550
                    FormatKey.MOST_SIGNIFICANT_IDENTIFIER, FormatKey.CLOSE_BRACKET });
551
        }
552
        if(CdmUtils.isBlank(specimenIdentifier)){
553
            specimenIdentifier = derivedUnit.getTitleCache();
554
        }
555
        if(CdmUtils.isBlank(specimenIdentifier)){
556
            specimenIdentifier = derivedUnit.getUuid().toString();
557
        }
558
        preservedSpecimenDTO.setAccessionNumber(specimenIdentifier);
559

    
560

    
561
        //preferred stable URI
562
        preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
563

    
564
        // citation
565
        Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit, null);
566
        if (fieldUnits.size() == 1) {
567
            preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
568
        }
569
        else{
570
            preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
571
        }
572

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

    
611
        // individuals associations
612
        Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
613
        for (IndividualsAssociation individualsAssociation : individualsAssociations) {
614
            if (individualsAssociation.getInDescription() != null) {
615
                if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
616
                    TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
617
                    Taxon taxon = taxonDescription.getTaxon();
618
                    if (taxon != null) {
619
                        preservedSpecimenDTO.addAssociatedTaxon(taxon);
620
                    }
621
                }
622
            }
623
        }
624
        // assemble sub derivates
625
        preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
626
        return preservedSpecimenDTO;
627
    }
628

    
629
    private DerivateDataDTO assembleDerivateDataDTO(SpecimenOrObservationBaseDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
630
        DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
631
        Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
632
        for (DerivedUnit childDerivate : childDerivates) {
633
            // assemble molecular data
634
            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
635
            if (childDerivate.isInstanceOf(DnaSample.class)) {
636
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
637
                    // TODO implement TissueSample assembly for web service
638
                }
639
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
640

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

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

    
687
                URI mediaUri = getMediaUri(media);
688
                if (media.getKindOfUnit() != null) {
689
                    // specimen scan
690
                    if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidSpecimenScan)) {
691
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
692
                        derivateDTO.setHasSpecimenScan(true);
693
                        String imageLinkText = "scan";
694
                        if (derivateDTO instanceof DerivedUnitDTO && ((DerivedUnitDTO) derivateDTO).getAccessionNumber() != null) {
695
                            imageLinkText = ((DerivedUnitDTO) derivateDTO).getAccessionNumber();
696
                        }
697
                        derivateDataDTO.addSpecimenScan(mediaUri, imageLinkText);
698
                    }
699
                    // detail image
700
                    else if (media.getKindOfUnit().getUuid().equals(DefinedTerm.uuidDetailImage)) {
701
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
702
                        derivateDTO.setHasDetailImage(true);
703
                        String motif = "detail image";
704
                        if (media.getMediaSpecimen()!=null){
705
                        	if(CdmUtils.isNotBlank(media.getMediaSpecimen().getTitleCache())) {
706
                        		motif = media.getMediaSpecimen().getTitleCache();
707
                        	}
708
                        }
709
                        derivateDataDTO.addDetailImage(mediaUri, motif);
710
                    }
711
                }
712
            }
713
        }
714
        return derivateDataDTO;
715
    }
716

    
717
    private String removeTail(String string, final String tail) {
718
        if (string.endsWith(tail)) {
719
            string = string.substring(0, string.length() - tail.length());
720
        }
721
        return string;
722
    }
723

    
724
    private URI getMediaUri(MediaSpecimen mediaSpecimen) {
725
        URI mediaUri = null;
726
        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
727
        if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
728
            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
729
            if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
730
                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
731
                if (part.getUri() != null) {
732
                    mediaUri = part.getUri();
733
                }
734
            }
735
        }
736
        return mediaUri;
737
    }
738

    
739
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
740
        Collection<DerivedUnit> derivedUnits = new ArrayList<>();
741
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
742
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
743
                derivedUnits.add(derivative);
744
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
745
            }
746
        }
747
        return derivedUnits;
748
    }
749

    
750
    private Set<SpecimenOrObservationBaseDTO> getDerivedUnitDTOsFor(SpecimenOrObservationBaseDTO specimenDto, DerivedUnit specimen, HashMap<UUID, SpecimenOrObservationBaseDTO> alreadyCollectedSpecimen) {
751
        Set<SpecimenOrObservationBaseDTO> derivedUnits = new HashSet<>();
752
//        load
753
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
754
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
755
                if (!alreadyCollectedSpecimen.containsKey(specimenDto.getUuid())){
756
                    DerivedUnitDTO dto;
757
                    if (derivative instanceof DnaSample) {
758
                        dto = new DNASampleDTO(derivative);
759
                    } else {
760
                        dto = new DerivedUnitDTO(derivative);
761
                    }
762
                    alreadyCollectedSpecimen.put(dto.getUuid(), dto);
763
                    dto.addAllDerivates(getDerivedUnitDTOsFor(dto, derivative, alreadyCollectedSpecimen));
764
                    derivedUnits.add(dto);
765
                }
766
            }
767
        }
768
        return derivedUnits;
769
    }
770

    
771
//    private Set<DerivateDTO> getDerivedUnitDTOsFor(DerivateDTO specimenDto, DerivedUnit specimen, HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen) {
772
//        Set<DerivateDTO> derivedUnits = new HashSet<>();
773
////        load
774
//        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
775
//            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
776
//                if (!alreadyCollectedSpecimen.containsKey(specimenDto.getUuid())){
777
//                    PreservedSpecimenDTO dto;
778
//                    if (derivative instanceof DnaSample){
779
//                        dto = DNASampleDTO.newInstance(derivative);
780
//                    }else{
781
//                        dto = PreservedSpecimenDTO.newInstance(derivative);
782
//                    }
783
//                    alreadyCollectedSpecimen.put(dto.getUuid(), dto);
784
//                    dto.addAllDerivates(getDerivedUnitDTOsFor(dto, derivative, alreadyCollectedSpecimen));
785
//                    derivedUnits.add(dto);
786
//                }
787
//            }
788
//        }
789
//        return derivedUnits;
790
//    }
791

    
792

    
793
    @SuppressWarnings("unchecked")
794
    @Override
795
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includedRelationships,
796
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
797

    
798
        Set<Taxon> taxa = new HashSet<>();
799
        Set<Integer> occurrenceIds = new HashSet<>();
800
        List<T> occurrences = new ArrayList<>();
801
        boolean includeUnpublished = INCLUDE_UNPUBLISHED;
802

    
803
        // Integer limit = PagerUtils.limitFor(pageSize);
804
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
805

    
806
        if (!getSession().contains(associatedTaxon)) {
807
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
808
        }
809

    
810
        if (includedRelationships != null) {
811
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includedRelationships, maxDepth, includeUnpublished, null, null, propertyPaths);
812
        }
813

    
814
        taxa.add(associatedTaxon);
815

    
816
        for (Taxon taxon : taxa) {
817
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
818
            for (SpecimenOrObservationBase<?> o : perTaxonOccurrences) {
819
                occurrenceIds.add(o.getId());
820
            }
821
        }
822
        occurrences = (List<T>) dao.loadList(occurrenceIds, null, propertyPaths);
823

    
824
        return new DefaultPagerImpl<T>(pageNumber, Long.valueOf(occurrences.size()), pageSize, occurrences);
825

    
826
    }
827

    
828
    @Override
829
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
830
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
831

    
832
        UUID uuid = UUID.fromString(taxonUUID);
833
        Taxon tax = (Taxon) taxonService.load(uuid);
834
        // TODO REMOVE NULL STATEMENT
835
        type = null;
836
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
837

    
838
    }
839

    
840
    @Override
841
    public List<FieldUnitDTO> findFieldUnitDTOByAssociatedTaxon(Set<TaxonRelationshipEdge> includedRelationships,
842
            UUID associatedTaxonUuid, List<String> propertyPaths) {
843

    
844
        Set<Taxon> taxa = new HashSet<>();
845
        Set<Integer> occurrenceIds = new HashSet<>();
846
        List<FieldUnitDTO> fieldUnitDTOs = new ArrayList<>();
847
        HashMap<UUID, SpecimenOrObservationBaseDTO> alreadyCollectedSpecimen = new HashMap<>();
848
        List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
849
        boolean includeUnpublished = INCLUDE_UNPUBLISHED;
850

    
851
        // Integer limit = PagerUtils.limitFor(pageSize);
852
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
853

    
854
        Taxon associatedTaxon = (Taxon) taxonService.load(associatedTaxonUuid);
855

    
856

    
857
        if (includedRelationships != null) {
858
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includedRelationships, null, includeUnpublished, null, null, null);
859
        }
860

    
861
        taxa.add(associatedTaxon);
862

    
863
        for (Taxon taxon : taxa) {
864
            List<SpecimenOrObservationBase> perTaxonOccurrences = dao.listByAssociatedTaxon(null,taxon, null, null, null, propertyPaths);
865
            for (SpecimenOrObservationBase<?> o : perTaxonOccurrences) {
866
                if (o.isInstanceOf(DerivedUnit.class)){
867
                    DerivedUnit derivedUnit;
868
                    SpecimenOrObservationBaseDTO derivedUnitDTO;
869
                    if (o.isInstanceOf(DnaSample.class)) {
870
                         derivedUnit = HibernateProxyHelper.deproxy(o, DnaSample.class);
871
                        derivedUnitDTO = new DNASampleDTO(derivedUnit);
872
                    } else {
873
                        derivedUnit = HibernateProxyHelper.deproxy(o, DerivedUnit.class);
874
                        derivedUnitDTO = new DerivedUnitDTO(derivedUnit);
875
                    }
876
                    if (alreadyCollectedSpecimen.get(derivedUnitDTO.getUuid()) == null){
877
                        alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
878
                    }
879
                    derivedUnitDTO.addAllDerivates(getDerivedUnitDTOsFor(derivedUnitDTO, derivedUnit, alreadyCollectedSpecimen));
880
                    this.findFieldUnitDTO(derivedUnitDTO, fieldUnitDTOs,
881
                            alreadyCollectedSpecimen);
882
                }
883
            }
884

    
885
        }
886

    
887
        return fieldUnitDTOs;
888

    
889
    }
890

    
891

    
892

    
893
    @Override
894
    public  FieldUnitDTO findByAccessionNumber(
895
            String accessionNumberString, List<OrderHint> orderHints,
896
            List<String> propertyPaths)  {
897

    
898
        DnaSample dnaSample = dao.findByGeneticAccessionNumber(accessionNumberString, propertyPaths);
899
        SpecimenOrObservationBaseDTO derivedUnitDTO;
900
        HashMap<UUID, SpecimenOrObservationBaseDTO> alreadyCollectedSpecimen = new HashMap<>();
901
        List<FieldUnitDTO> fieldUnitDTOs = new ArrayList<>();
902
        if (dnaSample != null){
903
            derivedUnitDTO = new DNASampleDTO(dnaSample);
904
            alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
905
            derivedUnitDTO.addAllDerivates(getDerivedUnitDTOsFor(derivedUnitDTO, dnaSample, alreadyCollectedSpecimen));
906
            FieldUnitDTO fieldUnit = this.findFieldUnitDTO(derivedUnitDTO, fieldUnitDTOs,
907
                    alreadyCollectedSpecimen);
908

    
909
            return fieldUnit;
910
        }
911
        return null;
912

    
913
    }
914

    
915
    @Override
916
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
917
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
918
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
919
            List<String> propertyPaths) throws IOException, LuceneParseException {
920

    
921
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
922

    
923
        // --- execute search
924
        TopGroups<BytesRef> topDocsResultSet;
925
        try {
926
            topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
927
        } catch (ParseException e) {
928
            LuceneParseException parseException = new LuceneParseException(e.getMessage());
929
            parseException.setStackTrace(e.getStackTrace());
930
            throw parseException;
931
        }
932

    
933
        Map<CdmBaseType, String> idFieldMap = new HashMap<>();
934
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
935

    
936
        // --- initialize taxa, highlight matches ....
937
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
938
        @SuppressWarnings("rawtypes")
939
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
940
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
941

    
942
        int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
943

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

    
946
    }
947

    
948
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
949
            List<Language> languages, boolean highlightFragments) {
950

    
951
        Builder finalQueryBuilder = new Builder();
952
        Builder textQueryBuilder = new Builder();
953

    
954
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
955
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
956

    
957
        // --- criteria
958
        luceneSearch.setCdmTypRestriction(clazz);
959
        if (queryString != null) {
960
            textQueryBuilder.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
961
            finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
962
        }
963

    
964
        // --- spacial query
965
        if (bbox != null) {
966
            finalQueryBuilder.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
967
        }
968

    
969
        luceneSearch.setQuery(finalQueryBuilder.build());
970

    
971
        // --- sorting
972
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false) };
973
        luceneSearch.setSortFields(sortFields);
974

    
975
        if (highlightFragments) {
976
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
977
        }
978
        return luceneSearch;
979
    }
980

    
981

    
982
    @Override
983
    public Collection<FieldUnit> findFieldUnits(UUID derivedUnitUuid, List<String> propertyPaths) {
984
        //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
985
        //from which this DerivedUnit was derived until all FieldUnits are found.
986

    
987
        // FIXME: use HQL queries to increase performance
988

    
989
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid, propertyPaths);
990
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
991
        Collection<FieldUnit> fieldUnits = new ArrayList<>();
992
        if (specimen == null){
993
            return null;
994
        }
995
        if (specimen.isInstanceOf(FieldUnit.class)) {
996
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
997
        }
998
        else if(specimen.isInstanceOf(DerivedUnit.class)){
999
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), propertyPaths));
1000
        }
1001
        return fieldUnits;
1002
    }
1003

    
1004
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit, List<String> propertyPaths) {
1005
        Collection<FieldUnit> fieldUnits = new HashSet<>();
1006
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
1007
        if (originals != null && !originals.isEmpty()) {
1008
            for (SpecimenOrObservationBase<?> original : originals) {
1009
                if (original.isInstanceOf(FieldUnit.class)) {
1010
                    fieldUnits.add(HibernateProxyHelper.deproxy(load(original.getUuid(), propertyPaths), FieldUnit.class));
1011
                }
1012
                else if(original.isInstanceOf(DerivedUnit.class)){
1013
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class), propertyPaths));
1014
                }
1015
            }
1016
        }
1017
        return fieldUnits;
1018
    }
1019

    
1020

    
1021

    
1022
    @Override
1023
    @Transactional(readOnly=true)
1024
    public FieldUnitDTO findFieldUnitDTO(SpecimenOrObservationBaseDTO derivedUnitDTO, Collection<FieldUnitDTO> fieldUnits,
1025
            HashMap<UUID, SpecimenOrObservationBaseDTO> alreadyCollectedSpecimen) {
1026
        //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
1027
        //from which this DerivedUnit was derived until all FieldUnits are found.
1028
        List<SpecimenOrObservationBase> specimens = new ArrayList<>();
1029
        List<String> propertyPaths = new ArrayList<>();
1030

    
1031
        propertyPaths.add("descriptions.elements.media.title");
1032
        propertyPaths.add("kindOfUnit");
1033
        propertyPaths.add("derivedFrom");
1034

    
1035
        specimens = dao.findOriginalsForDerivedUnit(derivedUnitDTO.getUuid(), propertyPaths);
1036

    
1037
        if (specimens.size() > 1){
1038
            logger.debug("The derived unit with uuid " + derivedUnitDTO.getUuid() + "has more than one orginal");
1039
        }
1040
      //  for (SpecimenOrObservationBase specimen: specimens){
1041
        SpecimenOrObservationBase specimen = null;
1042
        if (specimens.size() > 0){
1043
            specimen = specimens.get(0);
1044
        }else{
1045
            return null;
1046
        }
1047
        FieldUnitDTO fieldUnitDto = null;
1048
        if (alreadyCollectedSpecimen.get(specimen.getUuid()) != null){
1049
            alreadyCollectedSpecimen.get(specimen.getUuid()).addDerivate(derivedUnitDTO);
1050
//            if ( alreadyCollectedSpecimen.get(specimen.getUuid()) instanceof FieldUnitDTO){
1051
//                ((FieldUnitDTO)alreadyCollectedSpecimen.get(specimen.getUuid())).getTaxonRelatedDerivedUnits().add(derivedUnitDTO.getUuid());
1052
//            }
1053
        }else{
1054
            if (specimen.isInstanceOf(FieldUnit.class)){
1055
                fieldUnitDto = FieldUnitDTO.fromEntity((FieldUnit)specimen);
1056
                fieldUnitDto.addDerivate(derivedUnitDTO);
1057
                fieldUnits.add(fieldUnitDto);
1058

    
1059
            }else{
1060
                SpecimenOrObservationBaseDTO originalDTO;
1061
                if (specimen instanceof DnaSample){
1062
                    originalDTO = new DNASampleDTO((DnaSample)specimen);
1063
                } else {
1064
                    originalDTO = new DerivedUnitDTO((DerivedUnit)specimen);
1065
                }
1066
                originalDTO.addDerivate(derivedUnitDTO);
1067
                fieldUnitDto = findFieldUnitDTO(originalDTO, fieldUnits,
1068
                        alreadyCollectedSpecimen);
1069
            }
1070

    
1071
        }
1072
      //  }
1073
        alreadyCollectedSpecimen.put(derivedUnitDTO.getUuid(), derivedUnitDTO);
1074
//        if (fieldUnitDto != null){
1075
//            fieldUnitDto.addTaxonRelatedDerivedUnits(derivedUnitDTO);
1076
//        }
1077
        return fieldUnitDto;
1078

    
1079
    }
1080

    
1081
    @Override
1082
    @Transactional(readOnly=true)
1083
    public FieldUnitDTO loadFieldUnitDTO(UUID derivedUnitUuid) {
1084

    
1085
        FieldUnitDTO fieldUnitDTO = null;
1086
        SpecimenOrObservationBaseDTO derivedUnitDTO = null;
1087

    
1088
        Map<UUID, SpecimenOrObservationBaseDTO> cycleDetectionMap = new HashMap<>();
1089
        SpecimenOrObservationBase derivative = dao.load(derivedUnitUuid);
1090
        if(derivative != null){
1091
            if (derivative instanceof FieldUnit){
1092
                fieldUnitDTO = FieldUnitDTO.fromEntity((FieldUnit)derivative);
1093
                return fieldUnitDTO;
1094
            } else {
1095
                // must be a DerivedUnit otherwise
1096
                derivedUnitDTO = DerivedUnitDTO.fromEntity((DerivedUnit)derivative);
1097
                while(true){
1098

    
1099
                    Set<SpecimenOrObservationBaseDTO> originals = originalDTOs(derivedUnitDTO.getUuid());
1100

    
1101
                    if(originals.isEmpty()){
1102
                        break;
1103
                    }
1104
                    if (originals.size() > 1){
1105
                        logger.debug("The derived unit with uuid " + derivedUnitUuid + "has more than one orginal, ignoring all but the first one.");
1106
                    }
1107

    
1108
                    SpecimenOrObservationBaseDTO originalDTO = originals.iterator().next();
1109

    
1110
                    // cycle detection and handling
1111
                    if(cycleDetectionMap.containsKey(originalDTO.getUuid())){
1112
                        // cycle detected!!!
1113
                        try {
1114
                            throw new Exception();
1115
                        } catch(Exception e){
1116
                            logger.error("Cycle in derivate graph detected at DerivedUnit with uuid=" + originalDTO.getUuid() , e);
1117
                        }
1118
                        // to solve the situation for the output we remove the derivate from the more distant graph node
1119
                        // by removing it from the derivatives of its original
1120
                        // but let the derivate to be added to the original which is closer to the FieldUnit (below at originalDTO.addDerivate(derivedUnitDTO);)
1121
                        for(SpecimenOrObservationBaseDTO seenOriginal: cycleDetectionMap.values()){
1122
                            for(SpecimenOrObservationBaseDTO derivateDTO : seenOriginal.getDerivates()){
1123
                                if(derivateDTO.equals(originalDTO)){
1124
                                    seenOriginal.getDerivates().remove(originalDTO);
1125
                                }
1126
                            }
1127
                        }
1128

    
1129
                    } else {
1130
                        cycleDetectionMap.put(originalDTO.getUuid(), originalDTO);
1131
                    }
1132

    
1133

    
1134
                    if (originalDTO instanceof FieldUnitDTO){
1135
                        fieldUnitDTO = (FieldUnitDTO)originalDTO;
1136
                        if(derivedUnitDTO != null){
1137
                            fieldUnitDTO.addDerivate(derivedUnitDTO);
1138
                        }
1139
                        break;
1140
                    }else{
1141
                        if (derivedUnitDTO == null){
1142
                            derivedUnitDTO = originalDTO;
1143
                        } else {
1144
                            originalDTO.addDerivate(derivedUnitDTO);
1145
                            derivedUnitDTO = originalDTO;
1146
                        }
1147
                    }
1148
                }
1149
            }
1150
        }
1151
        return fieldUnitDTO;
1152

    
1153
    }
1154

    
1155
    /**
1156
     * @param originalDTO
1157
     * @return
1158
     */
1159
    private Set<SpecimenOrObservationBaseDTO> originalDTOs(UUID derivativeUuid) {
1160

    
1161
        Set<SpecimenOrObservationBaseDTO> dtos = new HashSet<>();
1162
        List<SpecimenOrObservationBase> specimens = dao.findOriginalsForDerivedUnit(derivativeUuid, null);
1163
        for(SpecimenOrObservationBase sob : specimens){
1164
            if(sob instanceof FieldUnit) {
1165
                dtos.add(FieldUnitDTO.fromEntity((FieldUnit)sob));
1166
            } else {
1167
                dtos.add(DerivedUnitDTO.fromEntity((DerivedUnit)sob));
1168
            }
1169
        }
1170
        return dtos;
1171
    }
1172

    
1173

    
1174
    @Override
1175
    @Transactional(readOnly = false)
1176
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
1177
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
1178
    }
1179

    
1180
    @Override
1181
    @Transactional(readOnly = false)
1182
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
1183
        // reload specimens to avoid session conflicts
1184
        DnaSample from = (DnaSample) load(fromUuid);
1185
        DnaSample to = (DnaSample) load(toUuid);
1186
        Sequence sequence = sequenceService.load(sequenceUuid);
1187

    
1188
        if (from == null || to == null || sequence == null) {
1189
            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" +
1190
                    "Operation was move "+sequence+ " from "+from+" to "+to);
1191
        }
1192
        UpdateResult result = new UpdateResult();
1193
        from.removeSequence(sequence);
1194
        saveOrUpdate(from);
1195
        to.addSequence(sequence);
1196
        saveOrUpdate(to);
1197
        result.setStatus(Status.OK);
1198
        result.addUpdatedObject(from);
1199
        result.addUpdatedObject(to);
1200
        return result;
1201
    }
1202

    
1203
    @Override
1204
    @Transactional(readOnly = false)
1205
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
1206
        return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
1207
    }
1208

    
1209
    @Override
1210
    @Transactional(readOnly = false)
1211
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
1212
        // reload specimens to avoid session conflicts
1213
        SpecimenOrObservationBase<?> from = null;
1214
        if(specimenFromUuid!=null){
1215
            from = load(specimenFromUuid);
1216
        }
1217
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
1218
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
1219

    
1220
        if ((specimenFromUuid!=null && from == null) || to == null || derivate == null) {
1221
            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" +
1222
                    "Operation was move "+derivate+ " from "+from+" to "+to);
1223
        }
1224
        UpdateResult result = new UpdateResult();
1225
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
1226
        SpecimenOrObservationType toType = to.getRecordBasis();
1227
        // check if type is a sub derivate type
1228
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
1229
                || derivateType==SpecimenOrObservationType.Media //moving media always works
1230
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
1231
            if(from!=null){
1232
                // remove derivation event from parent specimen of dragged object
1233
                DerivationEvent eventToRemove = null;
1234
                for (DerivationEvent event : from.getDerivationEvents()) {
1235
                    if (event.getDerivatives().contains(derivate)) {
1236
                        eventToRemove = event;
1237
                        break;
1238
                    }
1239
                }
1240
                from.removeDerivationEvent(eventToRemove);
1241
                if(eventToRemove!=null){
1242
                    // add new derivation event to target and copy the event parameters of the old one
1243
                    DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
1244
                    derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
1245
                    derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
1246
                    derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
1247
                    derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
1248
                    derivedFromNewOriginalEvent.setType(eventToRemove.getType());
1249
                    to.addDerivationEvent(derivedFromNewOriginalEvent);
1250
                    derivate.setDerivedFrom(derivedFromNewOriginalEvent);
1251
                }
1252
            }
1253
            else{
1254
                //derivative had no parent before so we use empty derivation event
1255
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
1256
                to.addDerivationEvent(derivedFromNewOriginalEvent);
1257
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
1258
            }
1259

    
1260
            if(from!=null){
1261
                saveOrUpdate(from);
1262
            }
1263
            saveOrUpdate(to);
1264
            result.setStatus(Status.OK);
1265
            result.addUpdatedObject(from);
1266
            result.addUpdatedObject(to);
1267
        } else {
1268
            result.setStatus(Status.ERROR);
1269
        }
1270
        return result;
1271
    }
1272

    
1273
    @Override
1274
    public DeleteResult isDeletable(UUID specimenUuid, DeleteConfiguratorBase config) {
1275
        DeleteResult deleteResult = new DeleteResult();
1276
        SpecimenOrObservationBase specimen = this.load(specimenUuid);
1277
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1278

    
1279
        // check elements found by super method
1280
        Set<CdmBase> relatedObjects = super.isDeletable(specimenUuid, config).getRelatedObjects();
1281
        for (CdmBase cdmBase : relatedObjects) {
1282
            // check for type designation
1283
            if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
1284
                deleteResult.setAbort();
1285
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is a type specimen."));
1286
                deleteResult.addRelatedObject(cdmBase);
1287
                break;
1288
            }
1289
            // check for IndividualsAssociations
1290
            else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
1291
                deleteResult.setAbort();
1292
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still associated via IndividualsAssociations"));
1293
                deleteResult.addRelatedObject(cdmBase);
1294
                break;
1295
            }
1296
            // check for taxon description
1297
            else if(cdmBase.isInstanceOf(TaxonDescription.class)
1298
                    && HibernateProxyHelper.deproxy(cdmBase, TaxonDescription.class).getDescribedSpecimenOrObservation().equals(specimen)
1299
                    && !specimenDeleteConfigurator.isDeleteFromDescription()){
1300
                deleteResult.setAbort();
1301
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still used as \"Described Specimen\" in a taxon description."));
1302
                deleteResult.addRelatedObject(cdmBase);
1303
                break;
1304
            }
1305
            // check for children and parents (derivation events)
1306
            else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1307
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1308
                // check if derivation event is empty
1309
                if (!derivationEvent.getDerivatives().isEmpty() && derivationEvent.getOriginals().contains(specimen)) {
1310
                    // if derivationEvent is the childEvent and contains derivations
1311
//                    if (derivationEvent.getDerivatives().contains(specimen)) {
1312
//                        //if it is the parent event the specimen is still deletable
1313
//                        continue;
1314
//                    }
1315
                    if(!specimenDeleteConfigurator.isDeleteChildren()){
1316
                        //if children should not be deleted then it is undeletable
1317
                        deleteResult.setAbort();
1318
                        deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation still has child derivatives."));
1319
                        deleteResult.addRelatedObject(cdmBase);
1320
                        break;
1321
                    }
1322
                    else{
1323
                        // check all children if they can be deleted
1324
                        Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1325
                        DeleteResult childResult = new DeleteResult();
1326
                        for (DerivedUnit derivedUnit : derivatives) {
1327
                            childResult.includeResult(isDeletable(derivedUnit.getUuid(), specimenDeleteConfigurator));
1328
                        }
1329
                        if (!childResult.isOk()) {
1330
                            deleteResult.setAbort();
1331
                            deleteResult.includeResult(childResult);
1332
                            deleteResult.addRelatedObject(cdmBase);
1333
                            break;
1334
                        }
1335
                    }
1336
                }
1337
            }
1338
            // check for amplification
1339
            else if (cdmBase.isInstanceOf(AmplificationResult.class)
1340
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1341
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1342
                deleteResult.setAbort();
1343
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1344
                deleteResult.addRelatedObject(cdmBase);
1345
                break;
1346
            }
1347
            // check for sequence
1348
            else if (cdmBase.isInstanceOf(Sequence.class)
1349
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1350
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1351
                deleteResult.setAbort();
1352
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1353
                deleteResult.addRelatedObject(cdmBase);
1354
                break;
1355
            }
1356
        }
1357
        if (deleteResult.isOk()) {
1358
            //add all related object if deletion is OK so they can be handled by the delete() method
1359
            deleteResult.addRelatedObjects(relatedObjects);
1360
        }
1361
        return deleteResult;
1362
    }
1363

    
1364
    /**
1365
     * {@inheritDoc}
1366
     */
1367
    @Transactional(readOnly = false)
1368
    @Override
1369
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1370
        return delete(load(specimenUuid), config);
1371
    }
1372

    
1373

    
1374
    @Transactional(readOnly = false)
1375
    @Override
1376
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1377
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1378

    
1379
        DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
1380
        if (!deleteResult.isOk()) {
1381
            return deleteResult;
1382
        }
1383

    
1384
        if (config.isDeleteChildren()) {
1385
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1386
            //clone to avoid concurrent modification
1387
            //can happen if the child is deleted and deleted its own derivedFrom event
1388
            Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
1389
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1390
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1391
                Iterator<DerivedUnit> it = derivatives.iterator();
1392
                Set<DerivedUnit> derivativesToDelete = new HashSet<>();
1393
                while (it.hasNext()) {
1394
                    DerivedUnit unit = it.next();
1395
                    derivativesToDelete.add(unit);
1396
                }
1397
                for (DerivedUnit unit:derivativesToDelete){
1398
                    deleteResult.includeResult(delete(unit, config));
1399
                }
1400
            }
1401
        }
1402

    
1403

    
1404

    
1405

    
1406
        // check related objects
1407
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1408

    
1409
        for (CdmBase relatedObject : relatedObjects) {
1410
            // check for TypeDesignations
1411
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1412
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1413
                designation.setTypeSpecimen(null);
1414
                List<TaxonName> typifiedNames = new ArrayList<>();
1415
                typifiedNames.addAll(designation.getTypifiedNames());
1416
                for (TaxonName taxonName : typifiedNames) {
1417
                    taxonName.removeTypeDesignation(designation);
1418
                }
1419
            }
1420
            // delete IndividualsAssociation
1421
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1422
                IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1423
                association.setAssociatedSpecimenOrObservation(null);
1424
                association.getInDescription().removeElement(association);
1425
            }
1426
            // check for "described specimen" (deprecated)
1427
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1428
                TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1429
                description.setDescribedSpecimenOrObservation(null);
1430
            }
1431
            // check for specimen description
1432
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1433
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1434
                specimenDescription.setDescribedSpecimenOrObservation(null);
1435
                // check if description is a description of the given specimen
1436
                if (specimen.getDescriptions().contains(specimenDescription)) {
1437
                    specimen.removeDescription(specimenDescription);
1438
                }
1439
                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription.getUuid(), null);
1440
                if (descriptionDelete.isOk()){
1441
                    deleteResult.includeResult(descriptionService.delete(specimenDescription));
1442
                }
1443
            }
1444
            // check for amplification
1445
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1446
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1447
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1448
            }
1449
            // check for sequence
1450
            if (relatedObject.isInstanceOf(Sequence.class)) {
1451
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1452
                sequence.getDnaSample().removeSequence(sequence);
1453
            }
1454
            // check for children and parents (derivation events)
1455
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1456
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1457
                // parent derivation event (derivedFrom)
1458
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1459
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1460
                    if (derivationEvent.getDerivatives().isEmpty()) {
1461
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1462
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1463
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1464
                            deleteResult.addUpdatedObject(specimenOrObservationBase);
1465
                        }
1466
                        // if derivationEvent has no derivates anymore, delete it
1467
                        deleteResult.includeResult(eventService.delete(derivationEvent));
1468
                    }
1469
                }
1470
                else{
1471
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1472
                }
1473
            }
1474
        }
1475
        if (specimen instanceof FieldUnit){
1476
            FieldUnit fieldUnit = HibernateProxyHelper.deproxy(specimen, FieldUnit.class);
1477
            GatheringEvent event = fieldUnit.getGatheringEvent();
1478
            fieldUnit.setGatheringEvent(null);
1479
            if (event != null){
1480
                DeleteResult result = eventService.isDeletable(event.getUuid(), null);
1481
                if (result.isOk()){
1482
                    deleteResult.includeResult( eventService.delete(event));
1483
                }
1484
            }
1485

    
1486
        }
1487
        deleteResult.includeResult(delete(specimen));
1488

    
1489
        return deleteResult;
1490
    }
1491

    
1492
    @Override
1493
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1494
        return dao.listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths);
1495
    }
1496

    
1497
    /**
1498
     * {@inheritDoc}
1499
     */
1500
    @Override
1501
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1502
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1503
        return listAssociatedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1504
    }
1505
    @Override
1506
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
1507
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1508
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1509

    
1510
        //individuals associations
1511
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
1512
        //type designation
1513
        if(specimen.isInstanceOf(DerivedUnit.class)){
1514
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class),
1515
                  includeUnpublished, limit, start, orderHints, propertyPaths));
1516
        }
1517
        //determinations
1518
        associatedTaxa.addAll(listDeterminedTaxa(specimen, includeUnpublished, limit, start, orderHints, propertyPaths));
1519

    
1520
        return associatedTaxa;
1521
    }
1522

    
1523

    
1524

    
1525
    /**
1526
     * {@inheritDoc}
1527
     */
1528
    @Override
1529
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1530
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1531
        return listDeterminedTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1532
    }
1533
    @Override
1534
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished, Integer limit, Integer start,
1535
            List<OrderHint> orderHints, List<String> propertyPaths) {
1536
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1537
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1538
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1539
                if(determinationEvent.getTaxon()!=null){
1540
                    associatedTaxa.add(taxonService.load(determinationEvent.getTaxon().getUuid(), includeUnpublished, propertyPaths));
1541
                }
1542
                if(determinationEvent.getTaxonName()!=null){
1543
                    Collection<TaxonBase> taxonBases = determinationEvent.getTaxonName().getTaxonBases();
1544
                    for (TaxonBase taxonBase : taxonBases) {
1545
                        associatedTaxa.add(taxonService.load(taxonBase.getUuid(), includeUnpublished, propertyPaths));
1546
                    }
1547
                }
1548
            }
1549
        }
1550
        return associatedTaxa;
1551
    }
1552

    
1553
    /**
1554
     * {@inheritDoc}
1555
     */
1556
    @Override
1557
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1558
            List<OrderHint> orderHints, List<String> propertyPaths) {
1559
        return listTypeDesignationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1560
    }
1561
    @Override
1562
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, boolean includeUnpublished,
1563
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1564
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1565
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1566
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1567
                Set<TaxonName> typifiedNames = typeDesignation.getTypifiedNames();
1568
                for (TaxonName taxonName : typifiedNames) {
1569
                    Set<Taxon> taxa = taxonName.getTaxa();
1570
                    for (Taxon taxon : taxa) {
1571
                        associatedTaxa.add(taxonService.load(taxon.getUuid(), includeUnpublished, propertyPaths));
1572
                    }
1573
                }
1574
            }
1575
        }
1576
        return associatedTaxa;
1577
    }
1578

    
1579
    /**
1580
     * {@inheritDoc}
1581
     */
1582
    @Override
1583
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit,
1584
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1585
        return listIndividualsAssociationTaxa(specimen, INCLUDE_UNPUBLISHED, limit, start, orderHints, propertyPaths);
1586
    }
1587

    
1588
    @Override
1589
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, boolean includeUnpublished,
1590
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1591
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1592
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, null, null, null, null)) {
1593
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1594
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1595
                if(taxonDescription.getTaxon()!=null){
1596
                    associatedTaxa.add(taxonService.load(taxonDescription.getTaxon().getUuid(), includeUnpublished, propertyPaths));
1597
                }
1598
            }
1599
        }
1600
        return associatedTaxa;
1601
    }
1602

    
1603
    @Override
1604
    public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1605
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1606
        return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1607
    }
1608

    
1609
    @Override
1610
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1611
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1612
            List<OrderHint> orderHints, List<String> propertyPaths) {
1613
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
1614
        for (DerivedUnit specimen : specimens) {
1615
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1616
            typeDesignationMap.put(specimen, typeDesignations);
1617
        }
1618
        return typeDesignationMap;
1619
    }
1620

    
1621
    @Override
1622
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1623
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1624
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1625
    }
1626

    
1627
    @Override
1628
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1629
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1630
            List<String> propertyPaths) {
1631
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1632
    }
1633

    
1634
    @Override
1635
    @Deprecated //this is not a service layer task so it may be removed in future versions
1636
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1637
        if (specimen != null) {
1638
            return specimen.characterData();
1639
        }else{
1640
            return new ArrayList<>();
1641
        }
1642
    }
1643

    
1644
    @Override
1645
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1646
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1647
        if (specimen != null) {
1648
            return getCharacterDataForSpecimen(specimen);
1649
        }
1650
        else{
1651
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1652
        }
1653
    }
1654

    
1655

    
1656
    @Override
1657
    public long countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1658
        if (config instanceof FindOccurrencesConfigurator) {
1659
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1660
            Taxon taxon = null;
1661
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1662
                TaxonBase<?> taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1663
                if(taxonBase.isInstanceOf(Taxon.class)){
1664
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1665
                }
1666
            }
1667
            TaxonName taxonName = null;
1668
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1669
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1670
            }
1671
            /*TODO: #6484 Neither isRetrieveIndirectlyAssociatedSpecimens() nor the AssignmentStatus
1672
             * is currently reflected in the HQL query. So using these in the count method will
1673
             * significantly slow down this method as we have to retrieve the entities instead of
1674
             * just the amount.
1675
             */
1676
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig.getAssignmentStatus().equals(AssignmentStatus.ALL_SPECIMENS)){
1677
                List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1678
                occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1679
                        occurrenceConfig.getTitleSearchStringSqlized(), occurrenceConfig.getSignificantIdentifier(),
1680
                        occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1681
                        occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1682
                occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1683
                return occurrences.size();
1684
            }
1685

    
1686
            return dao.countOccurrences(occurrenceConfig.getClazz(),
1687
                    occurrenceConfig.getTitleSearchStringSqlized(), occurrenceConfig.getSignificantIdentifier(),
1688
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1689
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1690
        }
1691
        else{
1692
            return super.countByTitle(config);
1693
        }
1694
    }
1695

    
1696
    @Override
1697
    public Pager<UuidAndTitleCache<SpecimenOrObservationBase>> findByTitleUuidAndTitleCache(
1698
            FindOccurrencesConfigurator config){
1699
        List<UuidAndTitleCache<SpecimenOrObservationBase>> occurrences = new ArrayList<>();
1700
        Taxon taxon = null;
1701
        if(config.getAssociatedTaxonUuid()!=null){
1702
            TaxonBase<?> taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
1703
            if(taxonBase.isInstanceOf(Taxon.class)){
1704
                taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1705
            }
1706
        }
1707
        TaxonName taxonName = null;
1708
        if(config.getAssociatedTaxonNameUuid()!=null){
1709
            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
1710
        }
1711
        occurrences.addAll(dao.findOccurrencesUuidAndTitleCache(config.getClazz(),
1712
                config.getTitleSearchString(), config.getSignificantIdentifier(),
1713
                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
1714
                config.getOrderHints()));
1715

    
1716
        return new DefaultPagerImpl<>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1717
    }
1718

    
1719
    @Override
1720
    public List<DerivedUnitDTO> findByTitlePreservedSpecimenDTO(FindOccurrencesConfigurator config) {
1721
        Taxon taxon = null;
1722
        if(config.getAssociatedTaxonUuid()!=null){
1723
            TaxonBase<?> taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
1724
            if(taxonBase.isInstanceOf(Taxon.class)){
1725
                taxon = CdmBase.deproxy(taxonBase, Taxon.class);
1726
            }
1727
        }
1728
        TaxonName taxonName = null;
1729
        if(config.getAssociatedTaxonNameUuid()!=null){
1730
            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
1731
        }
1732
        List<DerivedUnit> occurrences = new ArrayList<>();
1733
        occurrences.addAll(dao.findOccurrences(DerivedUnit.class,
1734
                config.getTitleSearchString(), config.getSignificantIdentifier(),
1735
                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
1736
                config.getOrderHints(), null));
1737

    
1738
        List<DerivedUnitDTO> dtos = new ArrayList<>();
1739
        occurrences.forEach(derivedUnit->dtos.add(assemblePreservedSpecimenDTO(derivedUnit)));
1740
        return dtos;
1741
    }
1742

    
1743
    @Override
1744
    public <S extends SpecimenOrObservationBase> Pager<S> findByTitle(
1745
            IIdentifiableEntityServiceConfigurator<S> config) {
1746
        if (config instanceof FindOccurrencesConfigurator) {
1747
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1748
            List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1749
            Taxon taxon = null;
1750
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1751
                TaxonBase<?> taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1752
                if(taxonBase.isInstanceOf(Taxon.class)){
1753
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1754
                }
1755
            }
1756
            TaxonName taxonName = null;
1757
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1758
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1759
            }
1760
            List<? extends SpecimenOrObservationBase> foundOccurrences = dao.findOccurrences(occurrenceConfig.getClazz(),
1761
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1762
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1763
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1764
            occurrences.addAll(foundOccurrences);
1765
            occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1766

    
1767
            return new DefaultPagerImpl<>(config.getPageNumber(), occurrences.size(), config.getPageSize(), (List<S>)occurrences);
1768
        }
1769
        return super.findByTitle(config);
1770
    }
1771

    
1772
    private List<SpecimenOrObservationBase> filterOccurencesByAssignmentAndHierarchy(
1773
            FindOccurrencesConfigurator occurrenceConfig, List<SpecimenOrObservationBase> occurrences, Taxon taxon,
1774
            TaxonName taxonName) {
1775
        //filter out (un-)assigned specimens
1776
        if(taxon==null && taxonName==null){
1777
            AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1778
            List<SpecimenOrObservationBase> specimenWithAssociations = new ArrayList<>();
1779
            if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1780
                for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1781
                    boolean includeUnpublished = true;  //TODO not sure if this is correct, maybe we have to propagate publish flag to higher methods.
1782
                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase,
1783
                            includeUnpublished, null, null, null, null);
1784
                    if(!associatedTaxa.isEmpty()){
1785
                        specimenWithAssociations.add(specimenOrObservationBase);
1786
                    }
1787
                }
1788
            }
1789
            if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1790
                occurrences.removeAll(specimenWithAssociations);
1791
            }
1792
            if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1793
                occurrences = new ArrayList<>(specimenWithAssociations);
1794
            }
1795
        }
1796
        // indirectly associated specimens
1797
        if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1798
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
1799
            for (SpecimenOrObservationBase<?> specimen : occurrences) {
1800
                List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1801
                for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1802
                    if(!occurrences.contains(specimenOrObservationBase)){
1803
                        indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1804
                    }
1805
                }
1806
            }
1807
            occurrences = indirectlyAssociatedOccurrences;
1808
        }
1809
        return occurrences;
1810
    }
1811

    
1812
    @Override
1813
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1814
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
1815
        Collection<FieldUnit> fieldUnits = findFieldUnits(specimen.getUuid(), null);
1816
        if(fieldUnits.isEmpty()){
1817
            allHierarchyDerivatives.add(specimen);
1818
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1819
        }
1820
        else{
1821
            for (FieldUnit fieldUnit : fieldUnits) {
1822
                allHierarchyDerivatives.add(fieldUnit);
1823
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1824
            }
1825
        }
1826
        return allHierarchyDerivatives;
1827
    }
1828

    
1829
    @Override
1830
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1831
        return getAllChildDerivatives(load(specimenUuid));
1832
    }
1833

    
1834
    @Override
1835
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1836
        if (specimen == null){
1837
            return null;
1838
        }
1839
        List<DerivedUnit> childDerivate = new ArrayList<>();
1840
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1841
        for (DerivationEvent derivationEvent : derivationEvents) {
1842
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1843
            for (DerivedUnit derivedUnit : derivatives) {
1844
                childDerivate.add(derivedUnit);
1845
                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1846
            }
1847
        }
1848
        return childDerivate;
1849
    }
1850

    
1851
    @Override
1852
    public long countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1853
        return countByTitle(config);
1854
    }
1855

    
1856
    /**
1857
     * {@inheritDoc}
1858
     */
1859
    @Override
1860
    public List<FieldUnit> findFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
1861
        return dao.findFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
1862
    }
1863

    
1864

    
1865
    /**
1866
     * {@inheritDoc}
1867
     */
1868
    @Override
1869
    public List<Point> findPointsForFieldUnitList(List<UUID> fieldUnitUuids) {
1870

    
1871
        return dao.findPointsForFieldUnitList(fieldUnitUuids);
1872
    }
1873
}
(76-76/100)