Project

General

Profile

Download (73.8 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.monitor.IProgressMonitor;
71
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
72
import eu.etaxonomy.cdm.model.CdmBaseType;
73
import eu.etaxonomy.cdm.model.agent.AgentBase;
74
import eu.etaxonomy.cdm.model.common.CdmBase;
75
import eu.etaxonomy.cdm.model.common.DefinedTerm;
76
import eu.etaxonomy.cdm.model.common.DefinedTermBase;
77
import eu.etaxonomy.cdm.model.common.ICdmBase;
78
import eu.etaxonomy.cdm.model.common.Language;
79
import eu.etaxonomy.cdm.model.description.CategoricalData;
80
import eu.etaxonomy.cdm.model.description.DescriptionBase;
81
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
82
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
83
import eu.etaxonomy.cdm.model.description.QuantitativeData;
84
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
85
import eu.etaxonomy.cdm.model.description.TaxonDescription;
86
import eu.etaxonomy.cdm.model.location.Country;
87
import eu.etaxonomy.cdm.model.location.NamedArea;
88
import eu.etaxonomy.cdm.model.media.Media;
89
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
90
import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
91
import eu.etaxonomy.cdm.model.media.MediaUtils;
92
import eu.etaxonomy.cdm.model.molecular.AmplificationResult;
93
import eu.etaxonomy.cdm.model.molecular.DnaSample;
94
import eu.etaxonomy.cdm.model.molecular.Sequence;
95
import eu.etaxonomy.cdm.model.molecular.SingleRead;
96
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
97
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
98
import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
99
import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
100
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
101
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
102
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
103
import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
104
import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
105
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
106
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
107
import eu.etaxonomy.cdm.model.taxon.Taxon;
108
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
109
import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
110
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
111
import eu.etaxonomy.cdm.persistence.dao.molecular.ISingleReadDao;
112
import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
113
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
114
import eu.etaxonomy.cdm.persistence.query.OrderHint;
115
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
116

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

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

    
127
    @Autowired
128
    private IDefinedTermDao definedTermDao;
129

    
130
    @Autowired
131
    private IDescriptionService descriptionService;
132

    
133
    @Autowired
134
    private INameService nameService;
135

    
136
    @Autowired
137
    private ITaxonService taxonService;
138

    
139
    @Autowired
140
    private ISequenceService sequenceService;
141

    
142
    @Autowired
143
    private ISingleReadDao singleReadDao;
144

    
145
    @Autowired
146
    private AbstractBeanInitializer beanInitializer;
147

    
148
    @Autowired
149
    private ILuceneIndexToolProvider luceneIndexToolProvider;
150

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

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

    
157

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

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

    
175
    }
176

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
340
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
341

    
342
        return derivedUnitFacadeList;
343
    }
344

    
345

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

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

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

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

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

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

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

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

    
388
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
389

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

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

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

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

    
462
        // assemble citation
463
        String citation = fieldUnit.getTitleCache();
464
        if (!preservedSpecimenAccessionNumbers.isEmpty()) {
465
            citation += " (";
466
            for (String accessionNumber : preservedSpecimenAccessionNumbers) {
467
                if (!accessionNumber.isEmpty()) {
468
                    citation += accessionNumber + SEPARATOR_STRING;
469
                }
470
            }
471
            citation = removeTail(citation, SEPARATOR_STRING);
472
            citation += ")";
473
        }
474
        fieldUnitDTO.setCitation(citation);
475

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

    
491
        return fieldUnitDTO;
492
    }
493

    
494
    @Override
495
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
496
        return assemblePreservedSpecimenDTO(derivedUnit, null);
497
    }
498

    
499
    @Override
500
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
501
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
502
            return derivedUnit.getAccessionNumber();
503
        }
504
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
505
            return derivedUnit.getBarcode();
506
        }
507
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
508
            return derivedUnit.getCatalogNumber();
509
        }
510
        return null;
511
    }
512

    
513
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
514
        if (!getSession().contains(derivedUnit)) {
515
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
516
        }
517
        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
518

    
519
        // check identifiers in priority order accNo>barCode>catalogNumber
520
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
521
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
522
        }
523
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
524
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
525
        }
526
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
527
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
528
        }
529
        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
530

    
531
        //preferred stable URI
532
        preservedSpecimenDTO.setPreferredStableUri(derivedUnit.getPreferredStableUri());
533

    
534
        // citation
535
        Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
536
        if (fieldUnits.size() == 1) {
537
            preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
538
        }
539
        else{
540
            preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
541
        }
542

    
543
        // character state data
544
        Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
545
        if (!characterDataForSpecimen.isEmpty()) {
546
            if (fieldUnitDTO != null) {
547
                fieldUnitDTO.setHasCharacterData(true);
548
            }
549
        }
550
        for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
551
            String character = descriptionElementBase.getFeature().getLabel();
552
            ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
553
            if (descriptionElementBase instanceof QuantitativeData) {
554
                QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
555
                DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
556
                String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
557
                preservedSpecimenDTO.addCharacterData(character, state);
558
            }
559
            else if(descriptionElementBase instanceof CategoricalData){
560
                CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
561
                DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
562
                String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
563
                preservedSpecimenDTO.addCharacterData(character, state);
564
            }
565
        }
566
        // check type designations
567
        Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
568
        for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
569
            if (fieldUnitDTO != null) {
570
                fieldUnitDTO.setHasType(true);
571
            }
572
            TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
573
            if (typeStatus != null) {
574
                List<String> typedTaxaNames = new ArrayList<String>();
575
                String label = typeStatus.getLabel();
576
                Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
577
                for (TaxonNameBase taxonNameBase : typifiedNames) {
578
                    typedTaxaNames.add(taxonNameBase.getFullTitleCache());
579
                }
580
                preservedSpecimenDTO.addTypes(label, typedTaxaNames);
581
            }
582
        }
583

    
584
        // individuals associations
585
        Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
586
        for (IndividualsAssociation individualsAssociation : individualsAssociations) {
587
            if (individualsAssociation.getInDescription() != null) {
588
                if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
589
                    TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
590
                    Taxon taxon = taxonDescription.getTaxon();
591
                    if (taxon != null) {
592
                        preservedSpecimenDTO.addAssociatedTaxon(taxon);
593
                    }
594
                }
595
            }
596
        }
597
        // assemble sub derivates
598
        preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
599
        return preservedSpecimenDTO;
600
    }
601

    
602
    private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
603
        DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
604
        Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
605
        for (DerivedUnit childDerivate : childDerivates) {
606
            // assemble molecular data
607
            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
608
            if (childDerivate.isInstanceOf(DnaSample.class)) {
609
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
610
                    // TODO implement TissueSample assembly for web service
611
                }
612
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
613

    
614
                    DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
615
                    if (!dna.getSequences().isEmpty()) {
616
                        derivateDTO.setHasDna(true);
617
                    }
618
                    for (Sequence sequence : dna.getSequences()) {
619
                        URI boldUri = null;
620
                        try {
621
                            boldUri = sequence.getBoldUri();
622
                        } catch (URISyntaxException e1) {
623
                            logger.error("Could not create BOLD URI", e1);
624
                        }
625
                        final DefinedTerm dnaMarker = sequence.getDnaMarker();
626
                        MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
627

    
628
                        //contig file
629
                        ContigFile contigFile = null;
630
                        if (sequence.getContigFile() != null) {
631
                            MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
632
                            if (contigMediaRepresentationPart != null) {
633
                                contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
634
                            }
635
                        }
636
                        if(contigFile==null){
637
                            contigFile = molecularData.addContigFile(null, "[no contig]");
638
                        }
639
                        // primer files
640
                        if (sequence.getSingleReads() != null) {
641
                            int readCount = 1;
642
                            for (SingleRead singleRead : sequence.getSingleReads()) {
643
                                MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
644
                                if (pherogramMediaRepresentationPart != null) {
645
                                    contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
646
                                }
647
                            }
648
                        }
649
                    }
650
                }
651
            }
652
            // assemble media data
653
            else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
654
                MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
655

    
656
                String mediaUriString = getMediaUriString(media);
657
                if (media.getKindOfUnit() != null) {
658
                    // specimen scan
659
                    if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
660
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
661
                        derivateDTO.setHasSpecimenScan(true);
662
                        String imageLinkText = "scan";
663
                        if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
664
                            imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
665
                        }
666
                        derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
667
                    }
668
                    // detail image
669
                    else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
670
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
671
                        derivateDTO.setHasDetailImage(true);
672
                        String motif = "";
673
                        if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
674
                            motif = media.getMediaSpecimen().getTitle().getText();
675
                        }
676
                        derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
677
                    }
678
                }
679
            }
680
        }
681
        return derivateDataDTO;
682
    }
683

    
684
    private String removeTail(String string, final String tail) {
685
        if (string.endsWith(tail)) {
686
            string = string.substring(0, string.length() - tail.length());
687
        }
688
        return string;
689
    }
690

    
691
    private String getMediaUriString(MediaSpecimen mediaSpecimen) {
692
        String mediaUri = null;
693
        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
694
        if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
695
            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
696
            if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
697
                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
698
                if (part.getUri() != null) {
699
                    mediaUri = part.getUri().toASCIIString();
700
                }
701
            }
702
        }
703
        return mediaUri;
704
    }
705

    
706
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
707
        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
708
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
709
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
710
                derivedUnits.add(derivative);
711
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
712
            }
713
        }
714
        return derivedUnits;
715
    }
716

    
717

    
718
    @SuppressWarnings("unchecked")
719
    @Override
720
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
721
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
722

    
723
        Set<Taxon> taxa = new HashSet<Taxon>();
724
        Set<Integer> occurrenceIds = new HashSet<Integer>();
725
        List<T> occurrences = new ArrayList<T>();
726

    
727
        // Integer limit = PagerUtils.limitFor(pageSize);
728
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
729

    
730
        if (!getSession().contains(associatedTaxon)) {
731
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
732
        }
733

    
734
        if (includeRelationships != null) {
735
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
736
        }
737

    
738
        taxa.add(associatedTaxon);
739

    
740
        for (Taxon taxon : taxa) {
741
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
742
            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
743
                occurrenceIds.add(o.getId());
744
            }
745
        }
746
        occurrences = (List<T>) dao.loadList(occurrenceIds, propertyPaths);
747

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

    
750
    }
751

    
752
    @Override
753
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
754
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
755

    
756
        UUID uuid = UUID.fromString(taxonUUID);
757
        Taxon tax = (Taxon) taxonService.load(uuid);
758
        // TODO REMOVE NULL STATEMENT
759
        type = null;
760
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
761

    
762
    }
763

    
764
    @Override
765
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
766
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
767
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
768
            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
769

    
770
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
771

    
772
        // --- execute search
773
        TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
774

    
775
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
776
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
777

    
778
        // --- initialize taxa, highlight matches ....
779
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
780
        @SuppressWarnings("rawtypes")
781
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
782
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
783

    
784
        int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
785

    
786
        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
787
                searchResults);
788

    
789
    }
790

    
791
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
792
            List<Language> languages, boolean highlightFragments) {
793

    
794
        Builder finalQueryBuilder = new Builder();
795
        Builder textQueryBuilder = new Builder();
796

    
797
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
798
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
799

    
800
        // --- criteria
801
        luceneSearch.setCdmTypRestriction(clazz);
802
        if (queryString != null) {
803
            textQueryBuilder.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
804
            finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
805
        }
806

    
807
        // --- spacial query
808
        if (bbox != null) {
809
            finalQueryBuilder.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
810
        }
811

    
812
        luceneSearch.setQuery(finalQueryBuilder.build());
813

    
814
        // --- sorting
815
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false) };
816
        luceneSearch.setSortFields(sortFields);
817

    
818
        if (highlightFragments) {
819
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
820
        }
821
        return luceneSearch;
822
    }
823

    
824

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

    
830
        // FIXME: use HQL queries to increase performance
831
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
832
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
833
        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
834

    
835
        if (specimen.isInstanceOf(FieldUnit.class)) {
836
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
837
        }
838
        else if(specimen.isInstanceOf(DerivedUnit.class)){
839
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
840
        }
841
        return fieldUnits;
842
    }
843

    
844
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
845
        Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
846
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
847
        if (originals != null && !originals.isEmpty()) {
848
            for (SpecimenOrObservationBase<?> original : originals) {
849
                if (original.isInstanceOf(FieldUnit.class)) {
850
                    fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
851
                }
852
                else if(original.isInstanceOf(DerivedUnit.class)){
853
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
854
                }
855
            }
856
        }
857
        return fieldUnits;
858
    }
859

    
860
    @Override
861
    @Transactional(readOnly = false)
862
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
863
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
864
    }
865

    
866
    @Override
867
    @Transactional(readOnly = false)
868
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
869
        // reload specimens to avoid session conflicts
870
        DnaSample from = (DnaSample) load(fromUuid);
871
        DnaSample to = (DnaSample) load(toUuid);
872
        Sequence sequence = sequenceService.load(sequenceUuid);
873

    
874
        if (from == null || to == null || sequence == null) {
875
            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" +
876
                    "Operation was move "+sequence+ " from "+from+" to "+to);
877
        }
878
        UpdateResult result = new UpdateResult();
879
        from.removeSequence(sequence);
880
        saveOrUpdate(from);
881
        to.addSequence(sequence);
882
        saveOrUpdate(to);
883
        result.setStatus(Status.OK);
884
        result.addUpdatedObject(from);
885
        result.addUpdatedObject(to);
886
        return result;
887
    }
888

    
889
    @Override
890
    @Transactional(readOnly = false)
891
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
892
        return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
893
    }
894

    
895
    @Override
896
    @Transactional(readOnly = false)
897
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
898
        // reload specimens to avoid session conflicts
899
        SpecimenOrObservationBase<?> from = null;
900
        if(specimenFromUuid!=null){
901
            from = load(specimenFromUuid);
902
        }
903
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
904
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
905

    
906
        if ((specimenFromUuid!=null && from == null) || to == null || derivate == null) {
907
            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" +
908
                    "Operation was move "+derivate+ " from "+from+" to "+to);
909
        }
910
        UpdateResult result = new UpdateResult();
911
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
912
        SpecimenOrObservationType toType = to.getRecordBasis();
913
        // check if type is a sub derivate type
914
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
915
                || derivateType==SpecimenOrObservationType.Media //moving media always works
916
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
917
            if(from!=null){
918
                // remove derivation event from parent specimen of dragged object
919
                DerivationEvent eventToRemove = null;
920
                for (DerivationEvent event : from.getDerivationEvents()) {
921
                    if (event.getDerivatives().contains(derivate)) {
922
                        eventToRemove = event;
923
                        break;
924
                    }
925
                }
926
                from.removeDerivationEvent(eventToRemove);
927
                if(eventToRemove!=null){
928
                    // add new derivation event to target and copy the event parameters of the old one
929
                    DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
930
                    derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
931
                    derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
932
                    derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
933
                    derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
934
                    derivedFromNewOriginalEvent.setType(eventToRemove.getType());
935
                    to.addDerivationEvent(derivedFromNewOriginalEvent);
936
                    derivate.setDerivedFrom(derivedFromNewOriginalEvent);
937
                }
938
            }
939
            else{
940
                //derivative had no parent before so we use empty derivation event
941
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
942
                to.addDerivationEvent(derivedFromNewOriginalEvent);
943
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
944
            }
945

    
946
            if(from!=null){
947
                saveOrUpdate(from);
948
            }
949
            saveOrUpdate(to);
950
            result.setStatus(Status.OK);
951
            result.addUpdatedObject(from);
952
            result.addUpdatedObject(to);
953
        } else {
954
            result.setStatus(Status.ERROR);
955
        }
956
        return result;
957
    }
958

    
959
    @Override
960
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
961
        // potential fields that are not persisted cascadingly
962
        /*
963
         * SOOB
964
        -DescriptionBase
965
        -determinations
966
        --modifier TERM
967
        -kindOfUnit TERM
968
        -lifeStage TERM
969
        -sex TERM
970

    
971
        FieldUnit
972
        -GatheringEvent
973
        --Country TERM
974
        --CollectingAreas TERM
975

    
976
        DerivedUnit
977
        -collection
978
        --institute
979
        ---types TERM
980
        -preservationMethod
981
        --medium TERM
982
        -storedUnder CDM TaxonNameBase
983
         */
984

    
985
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
986

    
987
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
988

    
989
        // FieldUnit
990
        if (specimen.isInstanceOf(FieldUnit.class)) {
991
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
992
        }
993
        // DerivedUnit
994
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
995
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
996
            if (derivedUnit.getDerivedFrom() != null) {
997
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
998
                for (FieldUnit fieldUnit : fieldUnits) {
999
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
1000
                }
1001
            }
1002
        }
1003
        return nonCascadedCdmEntities;
1004
    }
1005

    
1006
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
1007
        // get non cascaded element on SpecimenOrObservationBase level
1008
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
1009

    
1010
        // get FieldUnit specific elements
1011
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
1012
        if (gatheringEvent != null) {
1013
            // country
1014
            if (gatheringEvent.getCountry() != null) {
1015
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
1016
            }
1017
            // collecting areas
1018
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
1019
                nonCascadedCdmEntities.add(namedArea);
1020
            }
1021
        }
1022
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
1023
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1024
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
1025
            }
1026
        }
1027
        return nonCascadedCdmEntities;
1028
    }
1029

    
1030
    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
1031
        // get non cascaded element on SpecimenOrObservationBase level
1032
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
1033

    
1034
        // get DerivedUnit specific elements
1035
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
1036
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1037
                nonCascadedCdmEntities.add(type);
1038
            }
1039
        }
1040
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1041
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1042
        }
1043
        if (derivedUnit.getStoredUnder() != null) {
1044
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1045
        }
1046
        return nonCascadedCdmEntities;
1047
    }
1048

    
1049
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1050
            SpecimenOrObservationBase<?> specimen) {
1051
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1052
        // scan SpecimenOrObservationBase
1053
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1054
            // modifier
1055
            if (determinationEvent.getModifier() != null) {
1056
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
1057
            }
1058
        }
1059
        // kindOfUnit
1060
        if (specimen.getKindOfUnit() != null) {
1061
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1062
        }
1063
        // lifeStage
1064
        if (specimen.getLifeStage() != null) {
1065
            nonCascadedCdmEntities.add(specimen.getLifeStage());
1066
        }
1067
        // sex
1068
        if (specimen.getSex() != null) {
1069
            nonCascadedCdmEntities.add(specimen.getSex());
1070
        }
1071
        return nonCascadedCdmEntities;
1072
    }
1073

    
1074
    @Override
1075
    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
1076
        DeleteResult deleteResult = new DeleteResult();
1077
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1078

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

    
1164
    /**
1165
     * {@inheritDoc}
1166
     */
1167
    @Transactional(readOnly = false)
1168
    @Override
1169
    public DeleteResult delete(UUID specimenUuid, SpecimenDeleteConfigurator config) {
1170
        return delete(load(specimenUuid), config);
1171
    }
1172

    
1173

    
1174
    @Transactional(readOnly = false)
1175
    @Override
1176
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1177
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1178

    
1179
        if (config.isDeleteChildren()) {
1180
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1181
            //clone to avoid concurrent modification
1182
            //can happen if the child is deleted and deleted its own derivedFrom event
1183
            Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
1184
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1185
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1186
                for (DerivedUnit derivedUnit : derivatives) {
1187
                    delete(derivedUnit, config);
1188
                }
1189
            }
1190
        }
1191

    
1192
        DeleteResult deleteResult = isDeletable(specimen, config);
1193
        if (!deleteResult.isOk()) {
1194
            return deleteResult;
1195
        }
1196

    
1197
        // check related objects
1198
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1199

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

    
1265
        deleteResult.includeResult(delete(specimen));
1266

    
1267
        return deleteResult;
1268
    }
1269

    
1270
    @Override
1271
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1272
        return dao.listIndividualsAssociations(specimen, null, null, null, null);
1273
    }
1274

    
1275
    @Override
1276
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1277
            List<OrderHint> orderHints, List<String> propertyPaths) {
1278
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1279

    
1280
        //individuals associations
1281
        associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
1282
        //type designation
1283
        if(specimen.isInstanceOf(DerivedUnit.class)){
1284
            associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
1285
        }
1286
        //determinations
1287
        associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
1288

    
1289
        return associatedTaxa;
1290
    }
1291

    
1292

    
1293
    @Override
1294
    public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1295
            List<OrderHint> orderHints, List<String> propertyPaths) {
1296
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1297
        for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1298
            if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1299
                if(determinationEvent.getTaxon()!=null){
1300
                    associatedTaxa.add(determinationEvent.getTaxon());
1301
                }
1302
                if(determinationEvent.getTaxonName()!=null){
1303
                    associatedTaxa.addAll(determinationEvent.getTaxonName().getTaxonBases());
1304
                }
1305
            }
1306
        }
1307
        return associatedTaxa;
1308
    }
1309

    
1310
    @Override
1311
    public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1312
            List<OrderHint> orderHints, List<String> propertyPaths) {
1313
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1314
        for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1315
            if(typeDesignation.getTypeSpecimen().equals(specimen)){
1316
                Set<TaxonNameBase> typifiedNames = typeDesignation.getTypifiedNames();
1317
                for (TaxonNameBase taxonNameBase : typifiedNames) {
1318
                    associatedTaxa.addAll(taxonNameBase.getTaxa());
1319
                }
1320
            }
1321
        }
1322
        return associatedTaxa;
1323
    }
1324

    
1325
    @Override
1326
    public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1327
            List<OrderHint> orderHints, List<String> propertyPaths) {
1328
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1329
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1330
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1331
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1332
                if(taxonDescription.getTaxon()!=null){
1333
                    associatedTaxa.add(taxonDescription.getTaxon());
1334
                }
1335
            }
1336
        }
1337
        return associatedTaxa;
1338
    }
1339

    
1340
    @Override
1341
    public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1342
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1343
        return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1344
    }
1345

    
1346
    @Override
1347
    public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1348
            Collection<DerivedUnit> specimens, Integer limit, Integer start,
1349
            List<OrderHint> orderHints, List<String> propertyPaths) {
1350
        Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<DerivedUnit, Collection<SpecimenTypeDesignation>>();
1351
        for (DerivedUnit specimen : specimens) {
1352
            Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1353
            typeDesignationMap.put(specimen, typeDesignations);
1354
        }
1355
        return typeDesignationMap;
1356
    }
1357

    
1358
    @Override
1359
    public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1360
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1361
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1362
    }
1363

    
1364
    @Override
1365
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1366
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1367
            List<String> propertyPaths) {
1368
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1369
    }
1370

    
1371
    @Override
1372
    @Deprecated //this is not a service layer task so it may be removed in future versions
1373
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1374
        if (specimen != null) {
1375
            return specimen.characterData();
1376
        }else{
1377
            return new ArrayList<DescriptionElementBase>();
1378
        }
1379
    }
1380

    
1381
    @Override
1382
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1383
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1384
        if (specimen != null) {
1385
            return getCharacterDataForSpecimen(specimen);
1386
        }
1387
        else{
1388
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1389
        }
1390
    }
1391

    
1392

    
1393
    @Override
1394
    public Pager<SpecimenOrObservationBase> findByTitle(
1395
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1396
        if (config instanceof FindOccurrencesConfigurator) {
1397
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1398
            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1399
            Taxon taxon = null;
1400
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1401
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1402
                if(taxonBase.isInstanceOf(Taxon.class)){
1403
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1404
                }
1405
            }
1406
            TaxonNameBase taxonName = null;
1407
            if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1408
                taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1409
            }
1410
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1411
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1412
                    occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1413
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1414
            //filter out (un-)assigned specimens
1415
            if(taxon==null && taxonName==null){
1416
                AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1417
                List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<SpecimenOrObservationBase<?>>();
1418
                if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1419
                    for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1420
                        Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
1421
                        if(!associatedTaxa.isEmpty()){
1422
                            specimenWithAssociations.add(specimenOrObservationBase);
1423
                        }
1424
                    }
1425
                }
1426
                if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1427
                    occurrences.removeAll(specimenWithAssociations);
1428
                }
1429
                if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1430
                    occurrences = new ArrayList<SpecimenOrObservationBase>(specimenWithAssociations);
1431
                }
1432
            }
1433
            // indirectly associated specimens
1434
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1435
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1436
                for (SpecimenOrObservationBase specimen : occurrences) {
1437
                    List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1438
                    for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1439
                        if(!occurrences.contains(specimenOrObservationBase)){
1440
                            indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1441
                        }
1442
                    }
1443
                }
1444
                occurrences = indirectlyAssociatedOccurrences;
1445
            }
1446

    
1447
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1448
        }
1449
        return super.findByTitle(config);
1450
    }
1451

    
1452
    @Override
1453
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1454
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1455
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1456
        if(fieldUnits.isEmpty()){
1457
            allHierarchyDerivatives.add(specimen);
1458
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1459
        }
1460
        else{
1461
            for (FieldUnit fieldUnit : fieldUnits) {
1462
                allHierarchyDerivatives.add(fieldUnit);
1463
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1464
            }
1465
        }
1466
        return allHierarchyDerivatives;
1467
    }
1468

    
1469
    @Override
1470
    public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1471
        return getAllChildDerivatives(load(specimenUuid));
1472
    }
1473

    
1474
    @Override
1475
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1476
        List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1477
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1478
        for (DerivationEvent derivationEvent : derivationEvents) {
1479
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1480
            for (DerivedUnit derivedUnit : derivatives) {
1481
                childDerivate.add(derivedUnit);
1482
                childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1483
            }
1484
        }
1485
        return childDerivate;
1486
    }
1487

    
1488
    @Override
1489
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1490
        return findByTitle(config).getRecords().size();
1491
    }
1492

    
1493
}
(78-78/97)