Project

General

Profile

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

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

    
13
import java.io.IOException;
14
import java.net.URI;
15
import java.net.URISyntaxException;
16
import java.util.ArrayList;
17
import java.util.Arrays;
18
import java.util.Collection;
19
import java.util.Collections;
20
import java.util.HashMap;
21
import java.util.HashSet;
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.index.CorruptIndexException;
30
import org.apache.lucene.queryparser.classic.ParseException;
31
import org.apache.lucene.search.BooleanClause.Occur;
32
import org.apache.lucene.search.BooleanQuery.Builder;
33
import org.apache.lucene.search.SortField;
34
import org.apache.lucene.search.grouping.TopGroups;
35
import org.apache.lucene.util.BytesRef;
36
import org.hibernate.TransientObjectException;
37
import org.hibernate.search.spatial.impl.Rectangle;
38
import org.joda.time.Partial;
39
import org.springframework.beans.factory.annotation.Autowired;
40
import org.springframework.dao.DataRetrievalFailureException;
41
import org.springframework.stereotype.Service;
42
import org.springframework.transaction.annotation.Transactional;
43

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

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

    
126
    static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
127

    
128
    @Autowired
129
    private IDefinedTermDao definedTermDao;
130

    
131
    @Autowired
132
    private IDescriptionService descriptionService;
133

    
134
    @Autowired
135
    private INameService nameService;
136

    
137
    @Autowired
138
    private ITaxonService taxonService;
139

    
140
    @Autowired
141
    private ISequenceService sequenceService;
142

    
143
    @Autowired
144
    private ISingleReadDao singleReadDao;
145

    
146
    @Autowired
147
    private AbstractBeanInitializer beanInitializer;
148

    
149
    @Autowired
150
    private ILuceneIndexToolProvider luceneIndexToolProvider;
151

    
152
    private static final String SEPARATOR_STRING = ", ";
153

    
154
    public OccurrenceServiceImpl() {
155
        logger.debug("Load OccurrenceService Bean");
156
    }
157

    
158

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

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

    
176
    }
177

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

    
192
    @Override
193
    @Autowired
194
    protected void setDao(IOccurrenceDao dao) {
195
        this.dao = dao;
196
    }
197

    
198
    @Override
199
    public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
200
        Integer numberOfResults = dao.countDerivationEvents(occurence);
201

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

    
207
        return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
208
    }
209

    
210
    @Override
211
    public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
212
        return dao.countDeterminations(occurence, taxonbase);
213
    }
214

    
215
    @Override
216
    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
217
        Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
218

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

    
224
        return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
225
    }
226

    
227
    @Override
228
    public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
229
        Integer numberOfResults = dao.countMedia(occurence);
230

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

    
236
        return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
237
    }
238

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

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

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

    
299
    @Override
300
    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
301
        return dao.getDerivedUnitUuidAndTitleCache();
302
    }
303

    
304
    @Override
305
    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
306
        return dao.getFieldUnitUuidAndTitleCache();
307
    }
308

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

    
319
    @Override
320
    public List<DerivedUnitFacade> listDerivedUnitFacades(
321
            DescriptionBase description, List<String> propertyPaths) {
322

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

    
341
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
342

    
343
        return derivedUnitFacadeList;
344
    }
345

    
346

    
347
    @Override
348
    public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
349
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
350

    
351
        return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
352
    }
353

    
354
    @Override
355
    public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
356
        return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
357
    }
358

    
359
    @Override
360
    public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
361
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
362
            List<String> propertyPaths) {
363

    
364
        if (!getSession().contains(associatedTaxon)) {
365
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
366
        }
367

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

    
381
    @Override
382
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
383

    
384
        if (!getSession().contains(fieldUnit)) {
385
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
386
        }
387
        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
388

    
389
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
390

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

    
421
        // Taxon Name
422
        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
423

    
424
        // Herbaria map
425
        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
426
        // List of accession numbers for citation
427
        List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
428

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

    
463
        // assemble citation
464
        String citation = fieldUnit.getTitleCache();
465
        if((CdmUtils.isBlank(citation) || citation.contains("title cache generation not implemented"))
466
                && !fieldUnit.isProtectedTitleCache()){
467
            fieldUnit.setTitleCache(null);
468
            citation = fieldUnit.getTitleCache();
469
        }
470
        if (!preservedSpecimenAccessionNumbers.isEmpty()) {
471
            citation += " (";
472
            for (String accessionNumber : preservedSpecimenAccessionNumbers) {
473
                if (!accessionNumber.isEmpty()) {
474
                    citation += accessionNumber + SEPARATOR_STRING;
475
                }
476
            }
477
            citation = removeTail(citation, SEPARATOR_STRING);
478
            citation += ")";
479
        }
480
        fieldUnitDTO.setCitation(citation);
481

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

    
497
        return fieldUnitDTO;
498
    }
499

    
500
    @Override
501
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
502
        return assemblePreservedSpecimenDTO(derivedUnit, null);
503
    }
504

    
505
    @Override
506
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
507
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
508
            return derivedUnit.getAccessionNumber();
509
        }
510
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
511
            return derivedUnit.getBarcode();
512
        }
513
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
514
            return derivedUnit.getCatalogNumber();
515
        }
516
        return null;
517
    }
518

    
519
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
520
        if (!getSession().contains(derivedUnit)) {
521
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
522
        }
523
        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
524

    
525
        // check identifiers in priority order accNo>barCode>catalogNumber>collection
526
        String identifier = derivedUnit.getMostSignificantIdentifier();
527
        if(CdmUtils.isBlank(identifier) && derivedUnit.getCollection()!=null){
528
        	identifier = derivedUnit.getCollection().toString();
529
        }
530
        if(CdmUtils.isBlank(identifier)){
531
        	identifier = derivedUnit.getTitleCache();
532
        	if(CdmUtils.isBlank(identifier) && !derivedUnit.isProtectedTitleCache()){
533
        		//regenerate title cache if it is empty
534
        		derivedUnit.setTitleCache(null);
535
        		identifier = derivedUnit.getTitleCache();
536
        	}
537
        }
538
        if(CdmUtils.isBlank(identifier)){
539
        	//default fallback UUID
540
        	identifier = derivedUnit.getUuid().toString();
541
        }
542
        preservedSpecimenDTO.setAccessionNumber(identifier);
543
        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
544

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

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

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

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

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

    
628
                    DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
629
                    if (!dna.getSequences().isEmpty()) {
630
                        derivateDTO.setHasDna(true);
631
                    }
632
                    for (Sequence sequence : dna.getSequences()) {
633
                        URI boldUri = null;
634
                        try {
635
                            boldUri = sequence.getBoldUri();
636
                        } catch (URISyntaxException e1) {
637
                            logger.error("Could not create BOLD URI", e1);
638
                        }
639
                        final DefinedTerm dnaMarker = sequence.getDnaMarker();
640
                        MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
641

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

    
670
                URI mediaUri = getMediaUri(media);
671
                if (media.getKindOfUnit() != null) {
672
                    // specimen scan
673
                    if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
674
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
675
                        derivateDTO.setHasSpecimenScan(true);
676
                        String imageLinkText = "scan";
677
                        if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
678
                            imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
679
                        }
680
                        derivateDataDTO.addSpecimenScan(mediaUri, imageLinkText);
681
                    }
682
                    // detail image
683
                    else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
684
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
685
                        derivateDTO.setHasDetailImage(true);
686
                        String motif = "detail image";
687
                        if (media.getMediaSpecimen()!=null){
688
                        	if(CdmUtils.isNotBlank(media.getMediaSpecimen().getTitleCache())) {
689
                        		motif = media.getMediaSpecimen().getTitleCache();
690
                        	}
691
                        }
692
                        derivateDataDTO.addDetailImage(mediaUri, motif != null ? motif : "[no motif]");
693
                    }
694
                }
695
            }
696
        }
697
        return derivateDataDTO;
698
    }
699

    
700
    private String removeTail(String string, final String tail) {
701
        if (string.endsWith(tail)) {
702
            string = string.substring(0, string.length() - tail.length());
703
        }
704
        return string;
705
    }
706

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

    
722
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
723
        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
724
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
725
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
726
                derivedUnits.add(derivative);
727
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
728
            }
729
        }
730
        return derivedUnits;
731
    }
732

    
733

    
734
    @SuppressWarnings("unchecked")
735
    @Override
736
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
737
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
738

    
739
        Set<Taxon> taxa = new HashSet<Taxon>();
740
        Set<Integer> occurrenceIds = new HashSet<Integer>();
741
        List<T> occurrences = new ArrayList<T>();
742

    
743
        // Integer limit = PagerUtils.limitFor(pageSize);
744
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
745

    
746
        if (!getSession().contains(associatedTaxon)) {
747
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
748
        }
749

    
750
        if (includeRelationships != null) {
751
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
752
        }
753

    
754
        taxa.add(associatedTaxon);
755

    
756
        for (Taxon taxon : taxa) {
757
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
758
            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
759
                occurrenceIds.add(o.getId());
760
            }
761
        }
762
        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
763

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

    
766
    }
767

    
768
    @Override
769
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
770
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
771

    
772
        UUID uuid = UUID.fromString(taxonUUID);
773
        Taxon tax = (Taxon) taxonService.load(uuid);
774
        // TODO REMOVE NULL STATEMENT
775
        type = null;
776
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
777

    
778
    }
779

    
780
    @Override
781
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
782
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
783
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
784
            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
785

    
786
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
787

    
788
        // --- execute search
789
        TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
790

    
791
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
792
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
793

    
794
        // --- initialize taxa, highlight matches ....
795
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
796
        @SuppressWarnings("rawtypes")
797
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
798
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
799

    
800
        int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
801

    
802
        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
803
                searchResults);
804

    
805
    }
806

    
807
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
808
            List<Language> languages, boolean highlightFragments) {
809

    
810
        Builder finalQueryBuilder = new Builder();
811
        Builder textQueryBuilder = new Builder();
812

    
813
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
814
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
815

    
816
        // --- criteria
817
        luceneSearch.setCdmTypRestriction(clazz);
818
        if (queryString != null) {
819
            textQueryBuilder.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
820
            finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
821
        }
822

    
823
        // --- spacial query
824
        if (bbox != null) {
825
            finalQueryBuilder.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
826
        }
827

    
828
        luceneSearch.setQuery(finalQueryBuilder.build());
829

    
830
        // --- sorting
831
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false) };
832
        luceneSearch.setSortFields(sortFields);
833

    
834
        if (highlightFragments) {
835
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
836
        }
837
        return luceneSearch;
838
    }
839

    
840

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

    
846
        // FIXME: use HQL queries to increase performance
847
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
848
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
849
        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
850

    
851
        if (specimen.isInstanceOf(FieldUnit.class)) {
852
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
853
        }
854
        else if(specimen.isInstanceOf(DerivedUnit.class)){
855
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
856
        }
857
        return fieldUnits;
858
    }
859

    
860
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
861
        Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
862
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
863
        if (originals != null && !originals.isEmpty()) {
864
            for (SpecimenOrObservationBase<?> original : originals) {
865
                if (original.isInstanceOf(FieldUnit.class)) {
866
                    fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
867
                }
868
                else if(original.isInstanceOf(DerivedUnit.class)){
869
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
870
                }
871
            }
872
        }
873
        return fieldUnits;
874
    }
875

    
876
    @Override
877
    @Transactional(readOnly = false)
878
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
879
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
880
    }
881

    
882
    @Override
883
    @Transactional(readOnly = false)
884
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
885
        // reload specimens to avoid session conflicts
886
        DnaSample from = (DnaSample) load(fromUuid);
887
        DnaSample to = (DnaSample) load(toUuid);
888
        Sequence sequence = sequenceService.load(sequenceUuid);
889

    
890
        if (from == null || to == null || sequence == null) {
891
            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" +
892
                    "Operation was move "+sequence+ " from "+from+" to "+to);
893
        }
894
        UpdateResult result = new UpdateResult();
895
        from.removeSequence(sequence);
896
        saveOrUpdate(from);
897
        to.addSequence(sequence);
898
        saveOrUpdate(to);
899
        result.setStatus(Status.OK);
900
        result.addUpdatedObject(from);
901
        result.addUpdatedObject(to);
902
        return result;
903
    }
904

    
905
    @Override
906
    @Transactional(readOnly = false)
907
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
908
        return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
909
    }
910

    
911
    @Override
912
    @Transactional(readOnly = false)
913
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
914
        // reload specimens to avoid session conflicts
915
        SpecimenOrObservationBase<?> from = null;
916
        if(specimenFromUuid!=null){
917
            from = load(specimenFromUuid);
918
        }
919
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
920
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
921

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

    
962
            if(from!=null){
963
                saveOrUpdate(from);
964
            }
965
            saveOrUpdate(to);
966
            result.setStatus(Status.OK);
967
            result.addUpdatedObject(from);
968
            result.addUpdatedObject(to);
969
        } else {
970
            result.setStatus(Status.ERROR);
971
        }
972
        return result;
973
    }
974

    
975
    @Override
976
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
977
        // potential fields that are not persisted cascadingly
978
        /*
979
         * SOOB
980
        -DescriptionBase
981
        -determinations
982
        --modifier TERM
983
        -kindOfUnit TERM
984
        -lifeStage TERM
985
        -sex TERM
986

    
987
        FieldUnit
988
        -GatheringEvent
989
        --Country TERM
990
        --CollectingAreas TERM
991

    
992
        DerivedUnit
993
        -collection
994
        --institute
995
        ---types TERM
996
        -preservationMethod
997
        --medium TERM
998
        -storedUnder CDM TaxonNameBase
999
         */
1000

    
1001
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1002

    
1003
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
1004

    
1005
        // FieldUnit
1006
        if (specimen.isInstanceOf(FieldUnit.class)) {
1007
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
1008
        }
1009
        // DerivedUnit
1010
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
1011
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
1012
            if (derivedUnit.getDerivedFrom() != null) {
1013
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
1014
                for (FieldUnit fieldUnit : fieldUnits) {
1015
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
1016
                }
1017
            }
1018
        }
1019
        return nonCascadedCdmEntities;
1020
    }
1021

    
1022
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
1023
        // get non cascaded element on SpecimenOrObservationBase level
1024
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
1025

    
1026
        // get FieldUnit specific elements
1027
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
1028
        if (gatheringEvent != null) {
1029
            // country
1030
            if (gatheringEvent.getCountry() != null) {
1031
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
1032
            }
1033
            // collecting areas
1034
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
1035
                nonCascadedCdmEntities.add(namedArea);
1036
            }
1037
        }
1038
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
1039
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1040
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
1041
            }
1042
        }
1043
        return nonCascadedCdmEntities;
1044
    }
1045

    
1046
    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
1047
        // get non cascaded element on SpecimenOrObservationBase level
1048
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
1049

    
1050
        // get DerivedUnit specific elements
1051
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
1052
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1053
                nonCascadedCdmEntities.add(type);
1054
            }
1055
        }
1056
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1057
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1058
        }
1059
        if (derivedUnit.getStoredUnder() != null) {
1060
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1061
        }
1062
        return nonCascadedCdmEntities;
1063
    }
1064

    
1065
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1066
            SpecimenOrObservationBase<?> specimen) {
1067
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1068
        // scan SpecimenOrObservationBase
1069
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1070
            // modifier
1071
            if (determinationEvent.getModifier() != null) {
1072
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
1073
            }
1074
        }
1075
        // kindOfUnit
1076
        if (specimen.getKindOfUnit() != null) {
1077
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1078
        }
1079
        // lifeStage
1080
        if (specimen.getLifeStage() != null) {
1081
            nonCascadedCdmEntities.add(specimen.getLifeStage());
1082
        }
1083
        // sex
1084
        if (specimen.getSex() != null) {
1085
            nonCascadedCdmEntities.add(specimen.getSex());
1086
        }
1087
        return nonCascadedCdmEntities;
1088
    }
1089

    
1090
    @Override
1091
    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
1092
        DeleteResult deleteResult = new DeleteResult();
1093
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1094

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

    
1180
    /**
1181
     * {@inheritDoc}
1182
     */
1183
    @Transactional(readOnly = false)
1184
    @Override
1185
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1186
        return delete(load(specimenUuid), config);
1187
    }
1188

    
1189

    
1190
    @Transactional(readOnly = false)
1191
    @Override
1192
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1193
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1194

    
1195
        if (config.isDeleteChildren()) {
1196
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1197
            //clone to avoid concurrent modification
1198
            //can happen if the child is deleted and deleted its own derivedFrom event
1199
            Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
1200
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1201
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1202
                for (DerivedUnit derivedUnit : derivatives) {
1203
                    delete(derivedUnit, config);
1204
                }
1205
            }
1206
        }
1207

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

    
1213
        // check related objects
1214
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1215

    
1216
        for (CdmBase relatedObject : relatedObjects) {
1217
            // check for TypeDesignations
1218
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1219
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1220
                designation.setTypeSpecimen(null);
1221
                List<TaxonNameBase> typifiedNames = new ArrayList<TaxonNameBase>();
1222
                typifiedNames.addAll(designation.getTypifiedNames());
1223
                for (TaxonNameBase taxonNameBase : typifiedNames) {
1224
                    taxonNameBase.removeTypeDesignation(designation);
1225
                }
1226
            }
1227
            // delete IndividualsAssociation
1228
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1229
                IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1230
                association.setAssociatedSpecimenOrObservation(null);
1231
                association.getInDescription().removeElement(association);
1232
            }
1233
            // check for "described specimen" (deprecated)
1234
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1235
                TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1236
                description.setDescribedSpecimenOrObservation(null);
1237
            }
1238
            // check for specimen description
1239
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1240
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1241
                specimenDescription.setDescribedSpecimenOrObservation(null);
1242
                // check if description is a description of the given specimen
1243
                if (specimen.getDescriptions().contains(specimenDescription)) {
1244
                    specimen.removeDescription(specimenDescription);
1245
                }
1246
                DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription, null);
1247
                if (descriptionDelete.isOk()){
1248
                    descriptionService.delete(specimenDescription);
1249
                }
1250
            }
1251
            // check for amplification
1252
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1253
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1254
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1255
            }
1256
            // check for sequence
1257
            if (relatedObject.isInstanceOf(Sequence.class)) {
1258
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1259
                sequence.getDnaSample().removeSequence(sequence);
1260
            }
1261
            // check for children and parents (derivation events)
1262
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1263
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1264
                // parent derivation event (derivedFrom)
1265
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1266
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1267
                    if (derivationEvent.getDerivatives().isEmpty()) {
1268
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1269
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1270
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1271
                            deleteResult.addUpdatedObject(specimenOrObservationBase);
1272
                        }
1273
                    }
1274
                }
1275
                else{
1276
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1277
                }
1278
            }
1279
        }
1280

    
1281
        deleteResult.includeResult(delete(specimen));
1282

    
1283
        return deleteResult;
1284
    }
1285

    
1286
    @Override
1287
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1288
        return dao.listIndividualsAssociations(specimen, null, null, null, null);
1289
    }
1290

    
1291
    @Override
1292
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1293
            List<OrderHint> orderHints, List<String> propertyPaths) {
1294
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1295

    
1296
        //individuals associations
1297
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
1298
        //type designation
1299
        if(specimen.isInstanceOf(DerivedUnit.class)){
1300
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
1301
        }
1302
        //determinations
1303
        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
1304

    
1305
        return associatedTaxa;
1306
    }
1307

    
1308

    
1309
    @Override
1310
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1311
            List<OrderHint> orderHints, List<String> propertyPaths) {
1312
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1313
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1314
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1315
                if(determinationEvent.getTaxon()!=null){
1316
                    associatedTaxa.add(determinationEvent.getTaxon());
1317
                }
1318
                if(determinationEvent.getTaxonName()!=null){
1319
                    associatedTaxa.addAll(determinationEvent.getTaxonName().getTaxonBases());
1320
                }
1321
            }
1322
        }
1323
        return associatedTaxa;
1324
    }
1325

    
1326
    @Override
1327
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1328
            List<OrderHint> orderHints, List<String> propertyPaths) {
1329
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1330
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1331
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1332
                Set<TaxonNameBase> typifiedNames = typeDesignation.getTypifiedNames();
1333
                for (TaxonNameBase taxonNameBase : typifiedNames) {
1334
                    associatedTaxa.addAll(taxonNameBase.getTaxa());
1335
                }
1336
            }
1337
        }
1338
        return associatedTaxa;
1339
    }
1340

    
1341
    @Override
1342
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1343
            List<OrderHint> orderHints, List<String> propertyPaths) {
1344
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1345
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1346
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1347
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1348
                if(taxonDescription.getTaxon()!=null){
1349
                    associatedTaxa.add(taxonDescription.getTaxon());
1350
                }
1351
            }
1352
        }
1353
        return associatedTaxa;
1354
    }
1355

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

    
1362
    @Override
1363
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1364
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1365
            List<OrderHint> orderHints, List<String> propertyPaths) {
1366
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<DerivedUnit, Collection<SpecimenTypeDesignation>>();
1367
        for (DerivedUnit specimen : specimens) {
1368
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1369
            typeDesignationMap.put(specimen, typeDesignations);
1370
        }
1371
        return typeDesignationMap;
1372
    }
1373

    
1374
    @Override
1375
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1376
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1377
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1378
    }
1379

    
1380
    @Override
1381
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1382
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1383
            List<String> propertyPaths) {
1384
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1385
    }
1386

    
1387
    @Override
1388
    @Deprecated //this is not a service layer task so it may be removed in future versions
1389
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1390
        if (specimen != null) {
1391
            return specimen.characterData();
1392
        }else{
1393
            return new ArrayList<DescriptionElementBase>();
1394
        }
1395
    }
1396

    
1397
    @Override
1398
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1399
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1400
        if (specimen != null) {
1401
            return getCharacterDataForSpecimen(specimen);
1402
        }
1403
        else{
1404
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1405
        }
1406
    }
1407

    
1408

    
1409
    @Override
1410
    public Pager<SpecimenOrObservationBase> findByTitle(
1411
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1412
        if (config instanceof FindOccurrencesConfigurator) {
1413
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1414
            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1415
            Taxon taxon = null;
1416
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1417
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1418
                if(taxonBase.isInstanceOf(Taxon.class)){
1419
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1420
                }
1421
            }
1422
            TaxonNameBase taxonName = null;
1423
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1424
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1425
            }
1426
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1427
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1428
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1429
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1430
            //filter out (un-)assigned specimens
1431
            if(taxon==null && taxonName==null){
1432
                AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1433
                List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<SpecimenOrObservationBase<?>>();
1434
                if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1435
                    for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1436
                        Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
1437
                        if(!associatedTaxa.isEmpty()){
1438
                            specimenWithAssociations.add(specimenOrObservationBase);
1439
                        }
1440
                    }
1441
                }
1442
                if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1443
                    occurrences.removeAll(specimenWithAssociations);
1444
                }
1445
                if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1446
                    occurrences = new ArrayList<SpecimenOrObservationBase>(specimenWithAssociations);
1447
                }
1448
            }
1449
            // indirectly associated specimens
1450
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1451
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1452
                for (SpecimenOrObservationBase specimen : occurrences) {
1453
                    List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1454
                    for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1455
                        if(!occurrences.contains(specimenOrObservationBase)){
1456
                            indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1457
                        }
1458
                    }
1459
                }
1460
                occurrences = indirectlyAssociatedOccurrences;
1461
            }
1462

    
1463
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1464
        }
1465
        return super.findByTitle(config);
1466
    }
1467

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

    
1485
    @Override
1486
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1487
        return getAllChildDerivatives(load(specimenUuid));
1488
    }
1489

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

    
1504
    @Override
1505
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1506
        return findByTitle(config).getRecords().size();
1507
    }
1508

    
1509
}
(78-78/98)