Project

General

Profile

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

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

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

    
132
    @Autowired
133
    private IDefinedTermDao definedTermDao;
134

    
135
    @Autowired
136
    private IDescriptionService descriptionService;
137

    
138
    @Autowired
139
    private INameService nameService;
140

    
141
    @Autowired
142
    private IEventBaseService eventService;
143

    
144
    @Autowired
145
    private ITaxonService taxonService;
146

    
147
    @Autowired
148
    private ISequenceService sequenceService;
149

    
150
    @Autowired
151
    private ISingleReadDao singleReadDao;
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 void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
169
        if (clazz == null) {
170
            clazz = SpecimenOrObservationBase.class;
171
        }
172
        super.updateTitleCacheImpl(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.findByTitle(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
        Integer 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 int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
219
        return dao.countDeterminations(occurence, taxonbase);
220
    }
221

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
348
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
349

    
350
        return derivedUnitFacadeList;
351
    }
352

    
353

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

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

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

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

    
371
        if (!getSession().contains(associatedTaxon)) {
372
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
373
        }
374

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

    
388
    @Override
389
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
390

    
391
        if (!getSession().contains(fieldUnit)) {
392
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
393
        }
394
        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
395

    
396
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
397

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

    
428
        // Taxon Name
429
        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
430

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

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

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

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

    
504
        return fieldUnitDTO;
505
    }
506

    
507
    @Override
508
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
509
        return assemblePreservedSpecimenDTO(derivedUnit, null);
510
    }
511

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

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

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

    
547
        //preferred stable URI
548
        preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
549

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

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

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

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

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

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

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

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

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

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

    
739

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

    
745
        Set<Taxon> taxa = new HashSet<>();
746
        Set<Integer> occurrenceIds = new HashSet<>();
747
        List<T> occurrences = new ArrayList<>();
748

    
749
        // Integer limit = PagerUtils.limitFor(pageSize);
750
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
751

    
752
        if (!getSession().contains(associatedTaxon)) {
753
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
754
        }
755

    
756
        if (includeRelationships != null) {
757
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
758
        }
759

    
760
        taxa.add(associatedTaxon);
761

    
762
        for (Taxon taxon : taxa) {
763
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
764
            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
765
                occurrenceIds.add(o.getId());
766
            }
767
        }
768
        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
769

    
770
        return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
771

    
772
    }
773

    
774
    @Override
775
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
776
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
777

    
778
        UUID uuid = UUID.fromString(taxonUUID);
779
        Taxon tax = (Taxon) taxonService.load(uuid);
780
        // TODO REMOVE NULL STATEMENT
781
        type = null;
782
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
783

    
784
    }
785

    
786
    @Override
787
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
788
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
789
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
790
            List<String> propertyPaths) throws IOException, LuceneParseException {
791

    
792
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
793

    
794
        // --- execute search
795
        TopGroups<BytesRef> topDocsResultSet;
796
        try {
797
            topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
798
        } catch (ParseException e) {
799
            LuceneParseException parseException = new LuceneParseException(e.getMessage());
800
            parseException.setStackTrace(e.getStackTrace());
801
            throw parseException;
802
        }
803

    
804
        Map<CdmBaseType, String> idFieldMap = new HashMap<>();
805
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
806

    
807
        // --- initialize taxa, highlight matches ....
808
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
809
        @SuppressWarnings("rawtypes")
810
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
811
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
812

    
813
        int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
814

    
815
        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
816
                searchResults);
817

    
818
    }
819

    
820
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
821
            List<Language> languages, boolean highlightFragments) {
822

    
823
        Builder finalQueryBuilder = new Builder();
824
        Builder textQueryBuilder = new Builder();
825

    
826
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
827
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
828

    
829
        // --- criteria
830
        luceneSearch.setCdmTypRestriction(clazz);
831
        if (queryString != null) {
832
            textQueryBuilder.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
833
            finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
834
        }
835

    
836
        // --- spacial query
837
        if (bbox != null) {
838
            finalQueryBuilder.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
839
        }
840

    
841
        luceneSearch.setQuery(finalQueryBuilder.build());
842

    
843
        // --- sorting
844
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false) };
845
        luceneSearch.setSortFields(sortFields);
846

    
847
        if (highlightFragments) {
848
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
849
        }
850
        return luceneSearch;
851
    }
852

    
853

    
854
    @Override
855
    public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {
856
        //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
857
        //from which this DerivedUnit was derived until all FieldUnits are found.
858

    
859
        // FIXME: use HQL queries to increase performance
860
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
861
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
862
        Collection<FieldUnit> fieldUnits = new ArrayList<>();
863

    
864
        if (specimen.isInstanceOf(FieldUnit.class)) {
865
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
866
        }
867
        else if(specimen.isInstanceOf(DerivedUnit.class)){
868
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
869
        }
870
        return fieldUnits;
871
    }
872

    
873
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
874
        Collection<FieldUnit> fieldUnits = new HashSet<>();
875
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
876
        if (originals != null && !originals.isEmpty()) {
877
            for (SpecimenOrObservationBase<?> original : originals) {
878
                if (original.isInstanceOf(FieldUnit.class)) {
879
                    fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
880
                }
881
                else if(original.isInstanceOf(DerivedUnit.class)){
882
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
883
                }
884
            }
885
        }
886
        return fieldUnits;
887
    }
888

    
889
    @Override
890
    @Transactional(readOnly = false)
891
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
892
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
893
    }
894

    
895
    @Override
896
    @Transactional(readOnly = false)
897
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
898
        // reload specimens to avoid session conflicts
899
        DnaSample from = (DnaSample) load(fromUuid);
900
        DnaSample to = (DnaSample) load(toUuid);
901
        Sequence sequence = sequenceService.load(sequenceUuid);
902

    
903
        if (from == null || to == null || sequence == null) {
904
            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" +
905
                    "Operation was move "+sequence+ " from "+from+" to "+to);
906
        }
907
        UpdateResult result = new UpdateResult();
908
        from.removeSequence(sequence);
909
        saveOrUpdate(from);
910
        to.addSequence(sequence);
911
        saveOrUpdate(to);
912
        result.setStatus(Status.OK);
913
        result.addUpdatedObject(from);
914
        result.addUpdatedObject(to);
915
        return result;
916
    }
917

    
918
    @Override
919
    @Transactional(readOnly = false)
920
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
921
        return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
922
    }
923

    
924
    @Override
925
    @Transactional(readOnly = false)
926
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
927
        // reload specimens to avoid session conflicts
928
        SpecimenOrObservationBase<?> from = null;
929
        if(specimenFromUuid!=null){
930
            from = load(specimenFromUuid);
931
        }
932
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
933
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
934

    
935
        if ((specimenFromUuid!=null && from == null) || to == null || derivate == null) {
936
            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" +
937
                    "Operation was move "+derivate+ " from "+from+" to "+to);
938
        }
939
        UpdateResult result = new UpdateResult();
940
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
941
        SpecimenOrObservationType toType = to.getRecordBasis();
942
        // check if type is a sub derivate type
943
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
944
                || derivateType==SpecimenOrObservationType.Media //moving media always works
945
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
946
            if(from!=null){
947
                // remove derivation event from parent specimen of dragged object
948
                DerivationEvent eventToRemove = null;
949
                for (DerivationEvent event : from.getDerivationEvents()) {
950
                    if (event.getDerivatives().contains(derivate)) {
951
                        eventToRemove = event;
952
                        break;
953
                    }
954
                }
955
                from.removeDerivationEvent(eventToRemove);
956
                if(eventToRemove!=null){
957
                    // add new derivation event to target and copy the event parameters of the old one
958
                    DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
959
                    derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
960
                    derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
961
                    derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
962
                    derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
963
                    derivedFromNewOriginalEvent.setType(eventToRemove.getType());
964
                    to.addDerivationEvent(derivedFromNewOriginalEvent);
965
                    derivate.setDerivedFrom(derivedFromNewOriginalEvent);
966
                }
967
            }
968
            else{
969
                //derivative had no parent before so we use empty derivation event
970
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
971
                to.addDerivationEvent(derivedFromNewOriginalEvent);
972
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
973
            }
974

    
975
            if(from!=null){
976
                saveOrUpdate(from);
977
            }
978
            saveOrUpdate(to);
979
            result.setStatus(Status.OK);
980
            result.addUpdatedObject(from);
981
            result.addUpdatedObject(to);
982
        } else {
983
            result.setStatus(Status.ERROR);
984
        }
985
        return result;
986
    }
987

    
988
    @Override
989
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
990
        // potential fields that are not persisted cascadingly
991
        /*
992
         * SOOB
993
        -DescriptionBase
994
        -determinations
995
        --modifier TERM
996
        -kindOfUnit TERM
997
        -lifeStage TERM
998
        -sex TERM
999

    
1000
        FieldUnit
1001
        -GatheringEvent
1002
        --Country TERM
1003
        --CollectingAreas TERM
1004

    
1005
        DerivedUnit
1006
        -collection
1007
        --institute
1008
        ---types TERM
1009
        -preservationMethod
1010
        --medium TERM
1011
        -storedUnder CDM TaxonName
1012
         */
1013

    
1014
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<>();
1015

    
1016
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
1017

    
1018
        // FieldUnit
1019
        if (specimen.isInstanceOf(FieldUnit.class)) {
1020
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
1021
        }
1022
        // DerivedUnit
1023
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
1024
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
1025
            if (derivedUnit.getDerivedFrom() != null) {
1026
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
1027
                for (FieldUnit fieldUnit : fieldUnits) {
1028
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
1029
                }
1030
            }
1031
        }
1032
        return nonCascadedCdmEntities;
1033
    }
1034

    
1035
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
1036
        // get non cascaded element on SpecimenOrObservationBase level
1037
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
1038

    
1039
        // get FieldUnit specific elements
1040
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
1041
        if (gatheringEvent != null) {
1042
            // country
1043
            if (gatheringEvent.getCountry() != null) {
1044
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
1045
            }
1046
            // collecting areas
1047
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
1048
                nonCascadedCdmEntities.add(namedArea);
1049
            }
1050
        }
1051
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
1052
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1053
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
1054
            }
1055
        }
1056
        return nonCascadedCdmEntities;
1057
    }
1058

    
1059
    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
1060
        // get non cascaded element on SpecimenOrObservationBase level
1061
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
1062

    
1063
        // get DerivedUnit specific elements
1064
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
1065
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1066
                nonCascadedCdmEntities.add(type);
1067
            }
1068
        }
1069
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1070
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1071
        }
1072
        if (derivedUnit.getStoredUnder() != null) {
1073
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1074
        }
1075
        return nonCascadedCdmEntities;
1076
    }
1077

    
1078
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1079
            SpecimenOrObservationBase<?> specimen) {
1080
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<>();
1081
        // scan SpecimenOrObservationBase
1082
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1083
            // modifier
1084
            if (determinationEvent.getModifier() != null) {
1085
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
1086
            }
1087
        }
1088
        // kindOfUnit
1089
        if (specimen.getKindOfUnit() != null) {
1090
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1091
        }
1092
        // lifeStage
1093
        if (specimen.getLifeStage() != null) {
1094
            nonCascadedCdmEntities.add(specimen.getLifeStage());
1095
        }
1096
        // sex
1097
        if (specimen.getSex() != null) {
1098
            nonCascadedCdmEntities.add(specimen.getSex());
1099
        }
1100
        return nonCascadedCdmEntities;
1101
    }
1102

    
1103
    @Override
1104
    public DeleteResult isDeletable(UUID specimenUuid, DeleteConfiguratorBase config) {
1105
        DeleteResult deleteResult = new DeleteResult();
1106
        SpecimenOrObservationBase specimen = this.load(specimenUuid);
1107
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1108

    
1109
        // check elements found by super method
1110
        Set<CdmBase> relatedObjects = super.isDeletable(specimenUuid, config).getRelatedObjects();
1111
        for (CdmBase cdmBase : relatedObjects) {
1112
            // check for type designation
1113
            if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
1114
                deleteResult.setAbort();
1115
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is a type specimen."));
1116
                deleteResult.addRelatedObject(cdmBase);
1117
                break;
1118
            }
1119
            // check for IndividualsAssociations
1120
            else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
1121
                deleteResult.setAbort();
1122
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still associated via IndividualsAssociations"));
1123
                deleteResult.addRelatedObject(cdmBase);
1124
                break;
1125
            }
1126
            // check for taxon description
1127
            else if(cdmBase.isInstanceOf(TaxonDescription.class)
1128
                    && HibernateProxyHelper.deproxy(cdmBase, TaxonDescription.class).getDescribedSpecimenOrObservation().equals(specimen)
1129
                    && !specimenDeleteConfigurator.isDeleteFromDescription()){
1130
                deleteResult.setAbort();
1131
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still used as \"Described Specimen\" in a taxon description."));
1132
                deleteResult.addRelatedObject(cdmBase);
1133
                break;
1134
            }
1135
            // check for children and parents (derivation events)
1136
            else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1137
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1138
                // check if derivation event is empty
1139
                if (!derivationEvent.getDerivatives().isEmpty() && derivationEvent.getOriginals().contains(specimen)) {
1140
                    // if derivationEvent is the childEvent and contains derivations
1141
//                    if (derivationEvent.getDerivatives().contains(specimen)) {
1142
//                        //if it is the parent event the specimen is still deletable
1143
//                        continue;
1144
//                    }
1145
                    if(!specimenDeleteConfigurator.isDeleteChildren()){
1146
                        //if children should not be deleted then it is undeletable
1147
                        deleteResult.setAbort();
1148
                        deleteResult.addException(new ReferencedObjectUndeletableException("Specimen or obeservation still has child derivatives."));
1149
                        deleteResult.addRelatedObject(cdmBase);
1150
                        break;
1151
                    }
1152
                    else{
1153
                        // check all children if they can be deleted
1154
                        Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1155
                        DeleteResult childResult = new DeleteResult();
1156
                        for (DerivedUnit derivedUnit : derivatives) {
1157
                            childResult.includeResult(isDeletable(derivedUnit.getUuid(), specimenDeleteConfigurator));
1158
                        }
1159
                        if (!childResult.isOk()) {
1160
                            deleteResult.setAbort();
1161
                            deleteResult.includeResult(childResult);
1162
                            deleteResult.addRelatedObject(cdmBase);
1163
                            break;
1164
                        }
1165
                    }
1166
                }
1167
            }
1168
            // check for amplification
1169
            else if (cdmBase.isInstanceOf(AmplificationResult.class)
1170
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1171
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1172
                deleteResult.setAbort();
1173
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1174
                deleteResult.addRelatedObject(cdmBase);
1175
                break;
1176
            }
1177
            // check for sequence
1178
            else if (cdmBase.isInstanceOf(Sequence.class)
1179
                    && !specimenDeleteConfigurator.isDeleteMolecularData()
1180
                    && !specimenDeleteConfigurator.isDeleteChildren()) {
1181
                deleteResult.setAbort();
1182
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1183
                deleteResult.addRelatedObject(cdmBase);
1184
                break;
1185
            }
1186
        }
1187
        if (deleteResult.isOk()) {
1188
            //add all related object if deletion is OK so they can be handled by the delete() method
1189
            deleteResult.addRelatedObjects(relatedObjects);
1190
        }
1191
        return deleteResult;
1192
    }
1193

    
1194
    /**
1195
     * {@inheritDoc}
1196
     */
1197
    @Transactional(readOnly = false)
1198
    @Override
1199
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1200
        return delete(load(specimenUuid), config);
1201
    }
1202

    
1203

    
1204
    @Transactional(readOnly = false)
1205
    @Override
1206
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1207
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1208

    
1209
        DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
1210
        if (!deleteResult.isOk()) {
1211
            return deleteResult;
1212
        }
1213

    
1214
        if (config.isDeleteChildren()) {
1215
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1216
            //clone to avoid concurrent modification
1217
            //can happen if the child is deleted and deleted its own derivedFrom event
1218
            Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
1219
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1220
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1221
                Iterator<DerivedUnit> it = derivatives.iterator();
1222
                Set<DerivedUnit> derivativesToDelete = new HashSet<>();
1223
                while (it.hasNext()) {
1224
                    DerivedUnit unit = it.next();
1225
                    derivativesToDelete.add(unit);
1226
                }
1227
                for (DerivedUnit unit:derivativesToDelete){
1228
                    deleteResult.includeResult(delete(unit, config));
1229
                }
1230
            }
1231
        }
1232

    
1233

    
1234

    
1235

    
1236
        // check related objects
1237
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1238

    
1239
        for (CdmBase relatedObject : relatedObjects) {
1240
            // check for TypeDesignations
1241
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1242
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1243
                designation.setTypeSpecimen(null);
1244
                List<TaxonName> typifiedNames = new ArrayList<>();
1245
                typifiedNames.addAll(designation.getTypifiedNames());
1246
                for (TaxonName taxonName : typifiedNames) {
1247
                    taxonName.removeTypeDesignation(designation);
1248
                }
1249
            }
1250
            // delete IndividualsAssociation
1251
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1252
                IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1253
                association.setAssociatedSpecimenOrObservation(null);
1254
                association.getInDescription().removeElement(association);
1255
            }
1256
            // check for "described specimen" (deprecated)
1257
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1258
                TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1259
                description.setDescribedSpecimenOrObservation(null);
1260
            }
1261
            // check for specimen description
1262
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1263
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1264
                specimenDescription.setDescribedSpecimenOrObservation(null);
1265
                // check if description is a description of the given specimen
1266
                if (specimen.getDescriptions().contains(specimenDescription)) {
1267
                    specimen.removeDescription(specimenDescription);
1268
                }
1269
                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription.getUuid(), null);
1270
                if (descriptionDelete.isOk()){
1271
                    deleteResult.includeResult(descriptionService.delete(specimenDescription));
1272
                }
1273
            }
1274
            // check for amplification
1275
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1276
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1277
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1278
            }
1279
            // check for sequence
1280
            if (relatedObject.isInstanceOf(Sequence.class)) {
1281
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1282
                sequence.getDnaSample().removeSequence(sequence);
1283
            }
1284
            // check for children and parents (derivation events)
1285
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1286
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1287
                // parent derivation event (derivedFrom)
1288
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1289
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1290
                    if (derivationEvent.getDerivatives().isEmpty()) {
1291
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1292
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1293
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1294
                            deleteResult.addUpdatedObject(specimenOrObservationBase);
1295
                        }
1296
                        // if derivationEvent has no derivates anymore, delete it
1297
                        deleteResult.includeResult(eventService.delete(derivationEvent));
1298
                    }
1299
                }
1300
                else{
1301
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1302
                }
1303
            }
1304
        }
1305
        if (specimen instanceof FieldUnit){
1306
            FieldUnit fieldUnit = HibernateProxyHelper.deproxy(specimen, FieldUnit.class);
1307
            GatheringEvent event = fieldUnit.getGatheringEvent();
1308
            fieldUnit.setGatheringEvent(null);
1309
            if (event != null){
1310
                DeleteResult result = eventService.isDeletable(event.getUuid(), null);
1311
                if (result.isOk()){
1312
                    deleteResult.includeResult( eventService.delete(event));
1313
                }
1314
            }
1315

    
1316
        }
1317
        deleteResult.includeResult(delete(specimen));
1318

    
1319
        return deleteResult;
1320
    }
1321

    
1322
    @Override
1323
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1324
        return dao.listIndividualsAssociations(specimen, null, null, null, null);
1325
    }
1326

    
1327
    @Override
1328
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1329
            List<OrderHint> orderHints, List<String> propertyPaths) {
1330
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1331

    
1332
        //individuals associations
1333
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
1334
        //type designation
1335
        if(specimen.isInstanceOf(DerivedUnit.class)){
1336
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
1337
        }
1338
        //determinations
1339
        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
1340

    
1341
        return associatedTaxa;
1342
    }
1343

    
1344

    
1345
    @Override
1346
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1347
            List<OrderHint> orderHints, List<String> propertyPaths) {
1348
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1349
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1350
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1351
                if(determinationEvent.getTaxon()!=null){
1352
                    associatedTaxa.add(determinationEvent.getTaxon());
1353
                }
1354
                if(determinationEvent.getTaxonName()!=null){
1355
                    associatedTaxa.addAll((Collection)determinationEvent.getTaxonName().getTaxonBases());
1356
                }
1357
            }
1358
        }
1359
        return associatedTaxa;
1360
    }
1361

    
1362
    @Override
1363
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1364
            List<OrderHint> orderHints, List<String> propertyPaths) {
1365
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1366
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1367
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1368
                Set<TaxonName> typifiedNames = typeDesignation.getTypifiedNames();
1369
                for (TaxonName taxonName : typifiedNames) {
1370
                    associatedTaxa.addAll(taxonName.getTaxa());
1371
                }
1372
            }
1373
        }
1374
        return associatedTaxa;
1375
    }
1376

    
1377
    @Override
1378
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1379
            List<OrderHint> orderHints, List<String> propertyPaths) {
1380
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1381
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1382
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1383
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1384
                if(taxonDescription.getTaxon()!=null){
1385
                    associatedTaxa.add(taxonDescription.getTaxon());
1386
                }
1387
            }
1388
        }
1389
        return associatedTaxa;
1390
    }
1391

    
1392
    @Override
1393
    public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1394
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1395
        return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1396
    }
1397

    
1398
    @Override
1399
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1400
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1401
            List<OrderHint> orderHints, List<String> propertyPaths) {
1402
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
1403
        for (DerivedUnit specimen : specimens) {
1404
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1405
            typeDesignationMap.put(specimen, typeDesignations);
1406
        }
1407
        return typeDesignationMap;
1408
    }
1409

    
1410
    @Override
1411
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1412
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1413
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1414
    }
1415

    
1416
    @Override
1417
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1418
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1419
            List<String> propertyPaths) {
1420
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1421
    }
1422

    
1423
    @Override
1424
    @Deprecated //this is not a service layer task so it may be removed in future versions
1425
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1426
        if (specimen != null) {
1427
            return specimen.characterData();
1428
        }else{
1429
            return new ArrayList<>();
1430
        }
1431
    }
1432

    
1433
    @Override
1434
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1435
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1436
        if (specimen != null) {
1437
            return getCharacterDataForSpecimen(specimen);
1438
        }
1439
        else{
1440
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1441
        }
1442
    }
1443

    
1444

    
1445
    @Override
1446
    public Integer countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1447
        if (config instanceof FindOccurrencesConfigurator) {
1448
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1449
            Taxon taxon = null;
1450
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1451
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1452
                if(taxonBase.isInstanceOf(Taxon.class)){
1453
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1454
                }
1455
            }
1456
            TaxonName taxonName = null;
1457
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1458
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1459
            }
1460
            /*TODO: #6484 Neither isRetrieveIndirectlyAssociatedSpecimens() nor the AssignmentStatus
1461
             * is currently reflected in the HQL query. So using these in the count method will
1462
             * significantly slow down this method as we have to retreive the entities instead of
1463
             * the just the amount.
1464
             */
1465
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig.getAssignmentStatus().equals(AssignmentStatus.ALL_SPECIMENS)){
1466
                List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1467
                occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1468
                        occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1469
                        occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1470
                        occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1471
                occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1472
                return occurrences.size();
1473
            }
1474

    
1475
            return dao.countOccurrences(occurrenceConfig.getClazz(),
1476
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1477
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1478
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1479
        }
1480
        else{
1481
            return dao.countByTitle(config.getTitleSearchString());
1482
        }
1483
    }
1484

    
1485
    @Override
1486
    public Pager<SpecimenOrObservationBase> findByTitle(
1487
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1488
        if (config instanceof FindOccurrencesConfigurator) {
1489
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1490
            List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1491
            Taxon taxon = null;
1492
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1493
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1494
                if(taxonBase.isInstanceOf(Taxon.class)){
1495
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1496
                }
1497
            }
1498
            TaxonName taxonName = null;
1499
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1500
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1501
            }
1502
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1503
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1504
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1505
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1506
            occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1507

    
1508
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1509
        }
1510
        return super.findByTitle(config);
1511
    }
1512

    
1513
    private List<SpecimenOrObservationBase> filterOccurencesByAssignmentAndHierarchy(
1514
            FindOccurrencesConfigurator occurrenceConfig, List<SpecimenOrObservationBase> occurrences, Taxon taxon,
1515
            TaxonName taxonName) {
1516
        //filter out (un-)assigned specimens
1517
        if(taxon==null && taxonName==null){
1518
            AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1519
            List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
1520
            if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1521
                for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1522
                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
1523
                    if(!associatedTaxa.isEmpty()){
1524
                        specimenWithAssociations.add(specimenOrObservationBase);
1525
                    }
1526
                }
1527
            }
1528
            if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1529
                occurrences.removeAll(specimenWithAssociations);
1530
            }
1531
            if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1532
                occurrences = new ArrayList<>(specimenWithAssociations);
1533
            }
1534
        }
1535
        // indirectly associated specimens
1536
        if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1537
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
1538
            for (SpecimenOrObservationBase specimen : occurrences) {
1539
                List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1540
                for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1541
                    if(!occurrences.contains(specimenOrObservationBase)){
1542
                        indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1543
                    }
1544
                }
1545
            }
1546
            occurrences = indirectlyAssociatedOccurrences;
1547
        }
1548
        return occurrences;
1549
    }
1550

    
1551
    @Override
1552
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1553
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
1554
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1555
        if(fieldUnits.isEmpty()){
1556
            allHierarchyDerivatives.add(specimen);
1557
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1558
        }
1559
        else{
1560
            for (FieldUnit fieldUnit : fieldUnits) {
1561
                allHierarchyDerivatives.add(fieldUnit);
1562
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1563
            }
1564
        }
1565
        return allHierarchyDerivatives;
1566
    }
1567

    
1568
    @Override
1569
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1570
        return getAllChildDerivatives(load(specimenUuid));
1571
    }
1572

    
1573
    @Override
1574
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1575
        if (specimen == null){
1576
            return null;
1577
        }
1578
        List<DerivedUnit> childDerivate = new ArrayList<>();
1579
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1580
        for (DerivationEvent derivationEvent : derivationEvents) {
1581
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1582
            for (DerivedUnit derivedUnit : derivatives) {
1583
                childDerivate.add(derivedUnit);
1584
                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1585
            }
1586
        }
1587
        return childDerivate;
1588
    }
1589

    
1590
    @Override
1591
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1592
        return countByTitle(config);
1593
    }
1594

    
1595
    /**
1596
     * {@inheritDoc}
1597
     */
1598
    @Override
1599
    public List<FieldUnit> getFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
1600
        return dao.getFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
1601
    }
1602

    
1603
}
(81-81/105)