Project

General

Profile

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

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

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

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

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

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

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

    
131
    @Autowired
132
    private IDefinedTermDao definedTermDao;
133

    
134
    @Autowired
135
    private IDescriptionService descriptionService;
136

    
137
    @Autowired
138
    private INameService nameService;
139

    
140
    @Autowired
141
    private IEventBaseService eventService;
142

    
143
    @Autowired
144
    private ITaxonService taxonService;
145

    
146
    @Autowired
147
    private ISequenceService sequenceService;
148

    
149
    @Autowired
150
    private ISingleReadDao singleReadDao;
151

    
152
    @Autowired
153
    private AbstractBeanInitializer beanInitializer;
154

    
155
    @Autowired
156
    private ILuceneIndexToolProvider luceneIndexToolProvider;
157

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

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

    
164

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

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

    
182
    }
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
347
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
348

    
349
        return derivedUnitFacadeList;
350
    }
351

    
352

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

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

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

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

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

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

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

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

    
395
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
396

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

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

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

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

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

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

    
503
        return fieldUnitDTO;
504
    }
505

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
738

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

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

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

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

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

    
759
        taxa.add(associatedTaxon);
760

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

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

    
771
    }
772

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

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

    
783
    }
784

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

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

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

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

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

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

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

    
817
    }
818

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

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

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

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

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

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

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

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

    
852

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

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

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

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

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

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

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

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

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

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

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

    
987
    @Override
988
    public DeleteResult isDeletable(UUID specimenUuid, DeleteConfiguratorBase config) {
989
        DeleteResult deleteResult = new DeleteResult();
990
        SpecimenOrObservationBase specimen = this.load(specimenUuid);
991
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
992

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

    
1078
    /**
1079
     * {@inheritDoc}
1080
     */
1081
    @Transactional(readOnly = false)
1082
    @Override
1083
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1084
        return delete(load(specimenUuid), config);
1085
    }
1086

    
1087

    
1088
    @Transactional(readOnly = false)
1089
    @Override
1090
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1091
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1092

    
1093
        DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
1094
        if (!deleteResult.isOk()) {
1095
            return deleteResult;
1096
        }
1097

    
1098
        if (config.isDeleteChildren()) {
1099
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1100
            //clone to avoid concurrent modification
1101
            //can happen if the child is deleted and deleted its own derivedFrom event
1102
            Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
1103
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1104
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1105
                Iterator<DerivedUnit> it = derivatives.iterator();
1106
                Set<DerivedUnit> derivativesToDelete = new HashSet<>();
1107
                while (it.hasNext()) {
1108
                    DerivedUnit unit = it.next();
1109
                    derivativesToDelete.add(unit);
1110
                }
1111
                for (DerivedUnit unit:derivativesToDelete){
1112
                    deleteResult.includeResult(delete(unit, config));
1113
                }
1114
            }
1115
        }
1116

    
1117

    
1118

    
1119

    
1120
        // check related objects
1121
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1122

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

    
1200
        }
1201
        deleteResult.includeResult(delete(specimen));
1202

    
1203
        return deleteResult;
1204
    }
1205

    
1206
    @Override
1207
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1208
        return dao.listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths);
1209
    }
1210

    
1211
    @Override
1212
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1213
            List<OrderHint> orderHints, List<String> propertyPaths) {
1214
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1215

    
1216
        //individuals associations
1217
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
1218
        //type designation
1219
        if(specimen.isInstanceOf(DerivedUnit.class)){
1220
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
1221
        }
1222
        //determinations
1223
        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
1224

    
1225
        return associatedTaxa;
1226
    }
1227

    
1228

    
1229
    @Override
1230
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1231
            List<OrderHint> orderHints, List<String> propertyPaths) {
1232
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1233
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1234
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1235
                if(determinationEvent.getTaxon()!=null){
1236
                    associatedTaxa.add(taxonService.load(determinationEvent.getTaxon().getUuid(), propertyPaths));
1237
                }
1238
                if(determinationEvent.getTaxonName()!=null){
1239
                    Collection<TaxonBase> taxonBases = determinationEvent.getTaxonName().getTaxonBases();
1240
                    for (TaxonBase taxonBase : taxonBases) {
1241
                        associatedTaxa.add(taxonService.load(taxonBase.getUuid(), propertyPaths));
1242
                    }
1243
                }
1244
            }
1245
        }
1246
        return associatedTaxa;
1247
    }
1248

    
1249
    @Override
1250
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1251
            List<OrderHint> orderHints, List<String> propertyPaths) {
1252
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1253
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1254
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1255
                Set<TaxonName> typifiedNames = typeDesignation.getTypifiedNames();
1256
                for (TaxonName taxonName : typifiedNames) {
1257
                    Set<Taxon> taxa = taxonName.getTaxa();
1258
                    for (Taxon taxon : taxa) {
1259
                        associatedTaxa.add(taxonService.load(taxon.getUuid(), propertyPaths));
1260
                    }
1261
                }
1262
            }
1263
        }
1264
        return associatedTaxa;
1265
    }
1266

    
1267
    @Override
1268
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1269
            List<OrderHint> orderHints, List<String> propertyPaths) {
1270
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1271
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, null, null, null, null)) {
1272
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1273
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1274
                if(taxonDescription.getTaxon()!=null){
1275
                    associatedTaxa.add(taxonService.load(taxonDescription.getTaxon().getUuid(), propertyPaths));
1276
                }
1277
            }
1278
        }
1279
        return associatedTaxa;
1280
    }
1281

    
1282
    @Override
1283
    public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1284
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1285
        return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1286
    }
1287

    
1288
    @Override
1289
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1290
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1291
            List<OrderHint> orderHints, List<String> propertyPaths) {
1292
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
1293
        for (DerivedUnit specimen : specimens) {
1294
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1295
            typeDesignationMap.put(specimen, typeDesignations);
1296
        }
1297
        return typeDesignationMap;
1298
    }
1299

    
1300
    @Override
1301
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1302
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1303
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1304
    }
1305

    
1306
    @Override
1307
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1308
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1309
            List<String> propertyPaths) {
1310
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1311
    }
1312

    
1313
    @Override
1314
    @Deprecated //this is not a service layer task so it may be removed in future versions
1315
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1316
        if (specimen != null) {
1317
            return specimen.characterData();
1318
        }else{
1319
            return new ArrayList<>();
1320
        }
1321
    }
1322

    
1323
    @Override
1324
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1325
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1326
        if (specimen != null) {
1327
            return getCharacterDataForSpecimen(specimen);
1328
        }
1329
        else{
1330
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1331
        }
1332
    }
1333

    
1334

    
1335
    @Override
1336
    public Integer countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1337
        if (config instanceof FindOccurrencesConfigurator) {
1338
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1339
            Taxon taxon = null;
1340
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1341
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1342
                if(taxonBase.isInstanceOf(Taxon.class)){
1343
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1344
                }
1345
            }
1346
            TaxonName taxonName = null;
1347
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1348
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1349
            }
1350
            /*TODO: #6484 Neither isRetrieveIndirectlyAssociatedSpecimens() nor the AssignmentStatus
1351
             * is currently reflected in the HQL query. So using these in the count method will
1352
             * significantly slow down this method as we have to retreive the entities instead of
1353
             * the just the amount.
1354
             */
1355
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig.getAssignmentStatus().equals(AssignmentStatus.ALL_SPECIMENS)){
1356
                List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1357
                occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1358
                        occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1359
                        occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1360
                        occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1361
                occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1362
                return occurrences.size();
1363
            }
1364

    
1365
            return dao.countOccurrences(occurrenceConfig.getClazz(),
1366
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1367
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1368
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1369
        }
1370
        else{
1371
            return dao.countByTitle(config.getTitleSearchString());
1372
        }
1373
    }
1374

    
1375
    @Override
1376
    public Pager<UuidAndTitleCache<SpecimenOrObservationBase>> findByTitleUuidAndTitleCache(
1377
            FindOccurrencesConfigurator config){
1378
        List<UuidAndTitleCache<SpecimenOrObservationBase>> occurrences = new ArrayList<>();
1379
        Taxon taxon = null;
1380
        if(config.getAssociatedTaxonUuid()!=null){
1381
            TaxonBase taxonBase = taxonService.load(config.getAssociatedTaxonUuid());
1382
            if(taxonBase.isInstanceOf(Taxon.class)){
1383
                taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1384
            }
1385
        }
1386
        TaxonName taxonName = null;
1387
        if(config.getAssociatedTaxonNameUuid()!=null){
1388
            taxonName = nameService.load(config.getAssociatedTaxonNameUuid());
1389
        }
1390
        occurrences.addAll(dao.findOccurrencesUuidAndTitleCache(config.getClazz(),
1391
                config.getTitleSearchString(), config.getSignificantIdentifier(),
1392
                config.getSpecimenType(), taxon, taxonName, config.getMatchMode(), null, null,
1393
                config.getOrderHints()));
1394

    
1395
        return new DefaultPagerImpl<UuidAndTitleCache<SpecimenOrObservationBase>>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1396
    }
1397

    
1398
    @Override
1399
    public Pager<SpecimenOrObservationBase> findByTitle(
1400
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1401
        if (config instanceof FindOccurrencesConfigurator) {
1402
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1403
            List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1404
            Taxon taxon = null;
1405
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1406
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1407
                if(taxonBase.isInstanceOf(Taxon.class)){
1408
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1409
                }
1410
            }
1411
            TaxonName taxonName = null;
1412
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1413
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1414
            }
1415
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1416
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1417
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1418
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1419
            occurrences = filterOccurencesByAssignmentAndHierarchy(occurrenceConfig, occurrences, taxon, taxonName);
1420

    
1421
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1422
        }
1423
        return super.findByTitle(config);
1424
    }
1425

    
1426
    private List<SpecimenOrObservationBase> filterOccurencesByAssignmentAndHierarchy(
1427
            FindOccurrencesConfigurator occurrenceConfig, List<SpecimenOrObservationBase> occurrences, Taxon taxon,
1428
            TaxonName taxonName) {
1429
        //filter out (un-)assigned specimens
1430
        if(taxon==null && taxonName==null){
1431
            AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1432
            List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
1433
            if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1434
                for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1435
                    Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
1436
                    if(!associatedTaxa.isEmpty()){
1437
                        specimenWithAssociations.add(specimenOrObservationBase);
1438
                    }
1439
                }
1440
            }
1441
            if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1442
                occurrences.removeAll(specimenWithAssociations);
1443
            }
1444
            if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1445
                occurrences = new ArrayList<>(specimenWithAssociations);
1446
            }
1447
        }
1448
        // indirectly associated specimens
1449
        if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1450
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
1451
            for (SpecimenOrObservationBase specimen : occurrences) {
1452
                List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1453
                for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1454
                    if(!occurrences.contains(specimenOrObservationBase)){
1455
                        indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1456
                    }
1457
                }
1458
            }
1459
            occurrences = indirectlyAssociatedOccurrences;
1460
        }
1461
        return occurrences;
1462
    }
1463

    
1464
    @Override
1465
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1466
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
1467
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid(), null);
1468
        if(fieldUnits.isEmpty()){
1469
            allHierarchyDerivatives.add(specimen);
1470
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1471
        }
1472
        else{
1473
            for (FieldUnit fieldUnit : fieldUnits) {
1474
                allHierarchyDerivatives.add(fieldUnit);
1475
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1476
            }
1477
        }
1478
        return allHierarchyDerivatives;
1479
    }
1480

    
1481
    @Override
1482
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1483
        return getAllChildDerivatives(load(specimenUuid));
1484
    }
1485

    
1486
    @Override
1487
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1488
        if (specimen == null){
1489
            return null;
1490
        }
1491
        List<DerivedUnit> childDerivate = new ArrayList<>();
1492
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1493
        for (DerivationEvent derivationEvent : derivationEvents) {
1494
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1495
            for (DerivedUnit derivedUnit : derivatives) {
1496
                childDerivate.add(derivedUnit);
1497
                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1498
            }
1499
        }
1500
        return childDerivate;
1501
    }
1502

    
1503
    @Override
1504
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1505
        return countByTitle(config);
1506
    }
1507

    
1508
    /**
1509
     * {@inheritDoc}
1510
     */
1511
    @Override
1512
    public List<FieldUnit> getFieldUnitsForGatheringEvent(UUID gatheringEventUuid) {
1513
        return dao.getFieldUnitsForGatheringEvent(gatheringEventUuid, null, null, null, null);
1514
    }
1515

    
1516
}
(81-81/105)