Project

General

Profile

Download (70 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.ParseException;
31
import org.apache.lucene.search.BooleanClause.Occur;
32
import org.apache.lucene.search.BooleanQuery;
33
import org.apache.lucene.search.SortField;
34
import org.hibernate.TransientObjectException;
35
import org.hibernate.search.spatial.impl.Rectangle;
36
import org.joda.time.Partial;
37
import org.springframework.beans.factory.annotation.Autowired;
38
import org.springframework.dao.DataRetrievalFailureException;
39
import org.springframework.stereotype.Service;
40
import org.springframework.transaction.annotation.Transactional;
41

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

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

    
122
    static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
123

    
124
    @Autowired
125
    private IDefinedTermDao definedTermDao;
126

    
127
    @Autowired
128
    private IDescriptionService descriptionService;
129

    
130
    @Autowired
131
    private ITaxonService taxonService;
132

    
133
    @Autowired
134
    private ISequenceService sequenceService;
135

    
136
    @Autowired
137
    private AbstractBeanInitializer beanInitializer;
138

    
139
    @Autowired
140
    private ILuceneIndexToolProvider luceneIndexToolProvider;
141

    
142
    private static final String SEPARATOR_STRING = ", ";
143

    
144
    public OccurrenceServiceImpl() {
145
        logger.debug("Load OccurrenceService Bean");
146
    }
147

    
148

    
149
    @Override
150
    @Transactional(readOnly = false)
151
    public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
152
        if (clazz == null) {
153
            clazz = SpecimenOrObservationBase.class;
154
        }
155
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
156
    }
157

    
158
    /**
159
     * FIXME Candidate for harmonization
160
     * move to termService
161
     */
162
    @Override
163
    public Country getCountryByIso(String iso639) {
164
        return this.definedTermDao.getCountryByIso(iso639);
165

    
166
    }
167

    
168
    /**
169
     * FIXME Candidate for harmonization
170
     * move to termService
171
     */
172
    @Override
173
    public List<Country> getCountryByName(String name) {
174
        List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
175
        List<Country> countries = new ArrayList<Country>();
176
        for (int i = 0; i < terms.size(); i++) {
177
            countries.add((Country) terms.get(i));
178
        }
179
        return countries;
180
    }
181

    
182
    @Override
183
    @Autowired
184
    protected void setDao(IOccurrenceDao dao) {
185
        this.dao = dao;
186
    }
187

    
188
    @Override
189
    public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
190
        Integer numberOfResults = dao.countDerivationEvents(occurence);
191

    
192
        List<DerivationEvent> results = new ArrayList<DerivationEvent>();
193
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
194
            results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
195
        }
196

    
197
        return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
198
    }
199

    
200
    @Override
201
    public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
202
        return dao.countDeterminations(occurence, taxonbase);
203
    }
204

    
205
    @Override
206
    public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
207
        Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
208

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

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

    
217
    @Override
218
    public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
219
        Integer numberOfResults = dao.countMedia(occurence);
220

    
221
        List<Media> results = new ArrayList<Media>();
222
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
223
            results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
224
        }
225

    
226
        return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
227
    }
228

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

    
265
    @Override
266
    public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber,	List<OrderHint> orderHints, List<String> propertyPaths) {
267
        Integer numberOfResults = dao.count(type, determinedAs);
268
        List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
269
        pageNumber = pageNumber == null ? 0 : pageNumber;
270
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
271
            Integer start = pageSize == null ? 0 : pageSize * pageNumber;
272
            results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
273
        }
274
        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
275
    }
276

    
277
    @Override
278
    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
279
        return dao.getDerivedUnitUuidAndTitleCache();
280
    }
281

    
282
    @Override
283
    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
284
        return dao.getFieldUnitUuidAndTitleCache();
285
    }
286

    
287
    @Override
288
    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
289
        derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
290
        DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
291
        config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
292
        DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
293
        beanInitializer.initialize(derivedUnitFacade, propertyPaths);
294
        return derivedUnitFacade;
295
    }
296

    
297
    @Override
298
    public List<DerivedUnitFacade> listDerivedUnitFacades(
299
            DescriptionBase description, List<String> propertyPaths) {
300

    
301
        List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
302
        IndividualsAssociation tempIndividualsAssociation;
303
        SpecimenOrObservationBase tempSpecimenOrObservationBase;
304
        List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
305
        for (DescriptionElementBase element : elements) {
306
            if (element.isInstanceOf(IndividualsAssociation.class)) {
307
                tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
308
                if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
309
                    tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
310
                    if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
311
                        try {
312
                            derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
313
                        } catch (DerivedUnitFacadeNotSupportedException e) {
314
                            logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
315
                        }
316
                    }
317
                }
318

    
319
            }
320
        }
321

    
322
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
323

    
324
        return derivedUnitFacadeList;
325
    }
326

    
327

    
328
    @Override
329
    public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
330
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
331

    
332
        return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
333
    }
334

    
335
    @Override
336
    public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
337
        return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
338
    }
339

    
340
    @Override
341
    public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
342
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
343
            List<String> propertyPaths) {
344

    
345
        if (!getSession().contains(associatedTaxon)) {
346
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
347
        }
348

    
349
        // gather the IDs of all relevant field units
350
        Set<Integer> fieldUnitIds = new HashSet<Integer>();
351
        List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
352
        for (SpecimenOrObservationBase<?> specimen : records) {
353
            for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
354
                fieldUnitIds.add(fieldUnit.getId());
355
            }
356
        }
357
        //dao.listByIds() does the paging of the field units. Passing the field units directly to the Pager would not work
358
        List<SpecimenOrObservationBase> fieldUnits = dao.listByIds(fieldUnitIds, pageSize, pageNumber, orderHints, propertyPaths);
359
        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitIds.size(), pageSize, fieldUnits);
360
    }
361

    
362
    @Override
363
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
364

    
365
        if (!getSession().contains(fieldUnit)) {
366
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
367
        }
368
        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
369

    
370
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
371

    
372
        if (fieldUnit.getGatheringEvent() != null) {
373
            GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
374
            // Country
375
            NamedArea country = gatheringEvent.getCountry();
376
            fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
377
            // Collection
378
            AgentBase collector = gatheringEvent.getCollector();
379
            String fieldNumber = fieldUnit.getFieldNumber();
380
            String collectionString = "";
381
            if (collector != null || fieldNumber != null) {
382
                collectionString += collector != null ? collector : "";
383
                if (!collectionString.isEmpty()) {
384
                    collectionString += " ";
385
                }
386
                collectionString += (fieldNumber != null ? fieldNumber : "");
387
                collectionString.trim();
388
            }
389
            fieldUnitDTO.setCollection(collectionString);
390
            // Date
391
            Partial gatheringDate = gatheringEvent.getGatheringDate();
392
            String dateString = null;
393
            if (gatheringDate != null) {
394
                gatheringDate.toString();
395
            }
396
            else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
397
                dateString = gatheringEvent.getTimeperiod().getFreeText();
398
            }
399
            fieldUnitDTO.setDate(dateString);
400
        }
401

    
402
        // Taxon Name
403
        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
404

    
405
        // Herbaria map
406
        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
407
        // List of accession numbers for citation
408
        List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
409

    
410
        // assemble preserved specimen DTOs
411
        Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
412
        for (DerivationEvent derivationEvent : derivationEvents) {
413
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
414
            for (DerivedUnit derivedUnit : derivatives) {
415
                // collect accession numbers for citation
416
                String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
417
                if (mostSignificantIdentifier != null) {
418
                    preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
419
                }
420
                // collect collections for herbaria column
421
                if (derivedUnit.getCollection() != null) {
422
                    Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
423
                    if (herbariumCount == null) {
424
                        herbariumCount = 0;
425
                    }
426
                    collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
427
                }
428
                if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
429
                    PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
430
                    fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
431
                    fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
432
                    fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
433
                    fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
434
                    fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
435
                }
436
            }
437
        }
438
        // assemble derivate data DTO
439
        assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
440

    
441
        // assemble citation
442
        String citation = fieldUnit.getTitleCache();
443
        if (!preservedSpecimenAccessionNumbers.isEmpty()) {
444
            citation += " (";
445
            for (String accessionNumber : preservedSpecimenAccessionNumbers) {
446
                if (!accessionNumber.isEmpty()) {
447
                    citation += accessionNumber + SEPARATOR_STRING;
448
                }
449
            }
450
            citation = removeTail(citation, SEPARATOR_STRING);
451
            citation += ")";
452
        }
453
        fieldUnitDTO.setCitation(citation);
454

    
455
        // assemble herbaria string
456
        String herbariaString = "";
457
        for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
458
            eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
459
            if (collection.getCode() != null) {
460
                herbariaString += collection.getCode();
461
            }
462
            if (e.getValue() > 1) {
463
                herbariaString += "(" + e.getValue() + ")";
464
            }
465
            herbariaString += SEPARATOR_STRING;
466
        }
467
        herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
468
        fieldUnitDTO.setHerbarium(herbariaString);
469

    
470
        return fieldUnitDTO;
471
    }
472

    
473
    @Override
474
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
475
        return assemblePreservedSpecimenDTO(derivedUnit, null);
476
    }
477

    
478
    @Override
479
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
480
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
481
            return derivedUnit.getAccessionNumber();
482
        }
483
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
484
            return derivedUnit.getBarcode();
485
        }
486
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
487
            return derivedUnit.getCatalogNumber();
488
        }
489
        return null;
490
    }
491

    
492
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
493
        if (!getSession().contains(derivedUnit)) {
494
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
495
        }
496
        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
497

    
498
        // check identifiers in priority order accNo>barCode>catalogNumber
499
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
500
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
501
        }
502
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
503
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
504
        }
505
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
506
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
507
        }
508
        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
509

    
510
        // citation
511
        Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
512
        if (fieldUnits.size() == 1) {
513
            preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
514
        }
515
        else{
516
            preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
517
        }
518

    
519
        // character state data
520
        Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
521
        if (!characterDataForSpecimen.isEmpty()) {
522
            if (fieldUnitDTO != null) {
523
                fieldUnitDTO.setHasCharacterData(true);
524
            }
525
        }
526
        for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
527
            String character = descriptionElementBase.getFeature().getLabel();
528
            ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
529
            if (descriptionElementBase instanceof QuantitativeData) {
530
                QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
531
                DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
532
                String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
533
                preservedSpecimenDTO.addCharacterData(character, state);
534
            }
535
            else if(descriptionElementBase instanceof CategoricalData){
536
                CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
537
                DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
538
                String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
539
                preservedSpecimenDTO.addCharacterData(character, state);
540
            }
541
        }
542
        // check type designations
543
        Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
544
        for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
545
            if (fieldUnitDTO != null) {
546
                fieldUnitDTO.setHasType(true);
547
            }
548
            TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
549
            if (typeStatus != null) {
550
                List<String> typedTaxaNames = new ArrayList<String>();
551
                String label = typeStatus.getLabel();
552
                Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
553
                for (TaxonNameBase taxonNameBase : typifiedNames) {
554
                    typedTaxaNames.add(taxonNameBase.getFullTitleCache());
555
                }
556
                preservedSpecimenDTO.addTypes(label, typedTaxaNames);
557
            }
558
        }
559

    
560
        // individuals associations
561
        Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
562
        for (IndividualsAssociation individualsAssociation : individualsAssociations) {
563
            if (individualsAssociation.getInDescription() != null) {
564
                if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
565
                    TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
566
                    Taxon taxon = taxonDescription.getTaxon();
567
                    if (taxon != null && taxon.getName() != null) {
568
                        preservedSpecimenDTO.addAssociatedTaxon(taxon.getName().getTitleCache());
569
                    }
570
                }
571
            }
572
        }
573
        // assemble sub derivates
574
        preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
575
        return preservedSpecimenDTO;
576
    }
577

    
578
    private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
579
        DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
580
        Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
581
        for (DerivedUnit childDerivate : childDerivates) {
582
            // assemble molecular data
583
            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
584
            if (childDerivate.isInstanceOf(DnaSample.class)) {
585
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
586
                    // TODO implement TissueSample assembly for web service
587
                }
588
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
589

    
590
                    DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
591
                    if (!dna.getSequences().isEmpty()) {
592
                        derivateDTO.setHasDna(true);
593
                    }
594
                    for (Sequence sequence : dna.getSequences()) {
595
                        URI boldUri = null;
596
                        try {
597
                            boldUri = sequence.getBoldUri();
598
                        } catch (URISyntaxException e1) {
599
                            logger.error("Could not create BOLD URI", e1);
600
                        }
601
                        final DefinedTerm dnaMarker = sequence.getDnaMarker();
602
                        MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
603

    
604
                        //contig file
605
                        ContigFile contigFile = null;
606
                        if (sequence.getContigFile() != null) {
607
                            MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
608
                            if (contigMediaRepresentationPart != null) {
609
                                contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
610
                            }
611
                        }
612
                        if(contigFile==null){
613
                            contigFile = molecularData.addContigFile(null, "[no contig]");
614
                        }
615
                        // primer files
616
                        if (sequence.getSingleReads() != null) {
617
                            int readCount = 1;
618
                            for (SingleRead singleRead : sequence.getSingleReads()) {
619
                                MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
620
                                if (pherogramMediaRepresentationPart != null) {
621
                                    contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
622
                                }
623
                            }
624
                        }
625
                    }
626
                }
627
            }
628
            // assemble media data
629
            else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
630
                MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
631

    
632
                String mediaUriString = getMediaUriString(media);
633
                if (media.getKindOfUnit() != null) {
634
                    // specimen scan
635
                    if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
636
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
637
                        derivateDTO.setHasSpecimenScan(true);
638
                        String imageLinkText = "scan";
639
                        if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
640
                            imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
641
                        }
642
                        derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
643
                    }
644
                    // detail image
645
                    else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
646
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
647
                        derivateDTO.setHasDetailImage(true);
648
                        String motif = "";
649
                        if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
650
                            motif = media.getMediaSpecimen().getTitle().getText();
651
                        }
652
                        derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
653
                    }
654
                }
655
            }
656
        }
657
        return derivateDataDTO;
658
    }
659

    
660
    private String removeTail(String string, final String tail) {
661
        if (string.endsWith(tail)) {
662
            string = string.substring(0, string.length() - tail.length());
663
        }
664
        return string;
665
    }
666

    
667
    private String getMediaUriString(MediaSpecimen mediaSpecimen) {
668
        String mediaUri = null;
669
        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
670
        if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
671
            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
672
            if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
673
                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
674
                if (part.getUri() != null) {
675
                    mediaUri = part.getUri().toASCIIString();
676
                }
677
            }
678
        }
679
        return mediaUri;
680
    }
681

    
682
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
683
        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
684
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
685
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
686
                derivedUnits.add(derivative);
687
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
688
            }
689
        }
690
        return derivedUnits;
691
    }
692

    
693

    
694
    @SuppressWarnings("unchecked")
695
    @Override
696
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
697
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
698

    
699
        Set<Taxon> taxa = new HashSet<Taxon>();
700
        Set<Integer> occurrenceIds = new HashSet<Integer>();
701
        List<T> occurrences = new ArrayList<T>();
702

    
703
        // Integer limit = PagerUtils.limitFor(pageSize);
704
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
705

    
706
        if (!getSession().contains(associatedTaxon)) {
707
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
708
        }
709

    
710
        if (includeRelationships != null) {
711
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
712
        }
713

    
714
        taxa.add(associatedTaxon);
715

    
716
        for (Taxon taxon : taxa) {
717
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
718
            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
719
                occurrenceIds.add(o.getId());
720
            }
721
        }
722
        occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
723

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

    
726
    }
727

    
728
    @Override
729
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
730
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
731

    
732
        UUID uuid = UUID.fromString(taxonUUID);
733
        Taxon tax = (Taxon) taxonService.load(uuid);
734
        // TODO REMOVE NULL STATEMENT
735
        type = null;
736
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
737

    
738
    }
739

    
740
    @Override
741
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
742
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
743
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
744
            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
745

    
746
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
747

    
748
        // --- execute search
749
        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
750

    
751
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
752
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
753

    
754
        // --- initialize taxa, highlight matches ....
755
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
756
        @SuppressWarnings("rawtypes")
757
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
758
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
759

    
760
        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
761

    
762
        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
763
                searchResults);
764

    
765
    }
766

    
767
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
768
            List<Language> languages, boolean highlightFragments) {
769

    
770
        BooleanQuery finalQuery = new BooleanQuery();
771
        BooleanQuery textQuery = new BooleanQuery();
772

    
773
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
774
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
775

    
776
        // --- criteria
777
        luceneSearch.setCdmTypRestriction(clazz);
778
        if (queryString != null) {
779
            textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
780
            finalQuery.add(textQuery, Occur.MUST);
781
        }
782

    
783
        // --- spacial query
784
        if (bbox != null) {
785
            finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
786
        }
787

    
788
        luceneSearch.setQuery(finalQuery);
789

    
790
        // --- sorting
791
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false) };
792
        luceneSearch.setSortFields(sortFields);
793

    
794
        if (highlightFragments) {
795
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
796
        }
797
        return luceneSearch;
798
    }
799

    
800

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

    
806
        // FIXME: use HQL queries to increase performance
807
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
808
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
809
        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
810

    
811
        if (specimen.isInstanceOf(FieldUnit.class)) {
812
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
813
        }
814
        else if(specimen.isInstanceOf(DerivedUnit.class)){
815
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
816
        }
817
        return fieldUnits;
818
    }
819

    
820
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
821
        Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
822
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
823
        if (originals != null && !originals.isEmpty()) {
824
            for (SpecimenOrObservationBase<?> original : originals) {
825
                if (original.isInstanceOf(FieldUnit.class)) {
826
                    fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
827
                }
828
                else if(original.isInstanceOf(DerivedUnit.class)){
829
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
830
                }
831
            }
832
        }
833
        return fieldUnits;
834
    }
835

    
836
    @Override
837
    @Transactional(readOnly = false)
838
    public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
839
        return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
840
    }
841

    
842
    @Override
843
    @Transactional(readOnly = false)
844
    public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
845
        // reload specimens to avoid session conflicts
846
        DnaSample from = (DnaSample) load(fromUuid);
847
        DnaSample to = (DnaSample) load(toUuid);
848
        Sequence sequence = sequenceService.load(sequenceUuid);
849

    
850
        if (from == null || to == null || sequence == null) {
851
            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" +
852
                    "Operation was move "+sequence+ " from "+from+" to "+to);
853
        }
854
        UpdateResult result = new UpdateResult();
855
        from.removeSequence(sequence);
856
        saveOrUpdate(from);
857
        to.addSequence(sequence);
858
        saveOrUpdate(to);
859
        result.setStatus(Status.OK);
860
        result.addUpdatedObject(from);
861
        result.addUpdatedObject(to);
862
        return result;
863
    }
864

    
865
    @Override
866
    @Transactional(readOnly = false)
867
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
868
        return moveDerivate(from.getUuid(), to.getUuid(), derivate.getUuid()).isOk();
869
    }
870

    
871
    @Override
872
    @Transactional(readOnly = false)
873
    public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
874
        // reload specimens to avoid session conflicts
875
        SpecimenOrObservationBase<?> from = load(specimenFromUuid);
876
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
877
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
878

    
879
        if (from == null || to == null || derivate == null) {
880
            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" +
881
            		"Operation was move "+derivate+ " from "+from+" to "+to);
882
        }
883
        UpdateResult result = new UpdateResult();
884
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
885
        SpecimenOrObservationType toType = to.getRecordBasis();
886
        // check if type is a sub derivate type
887
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
888
                || derivateType==SpecimenOrObservationType.Media //moving media always works
889
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
890
            // remove derivation event from parent specimen of dragged object
891
            DerivationEvent eventToRemove = null;
892
            for (DerivationEvent event : from.getDerivationEvents()) {
893
                if (event.getDerivatives().contains(derivate)) {
894
                    eventToRemove = event;
895
                    break;
896
                }
897
            }
898
            from.removeDerivationEvent(eventToRemove);
899
            if(eventToRemove!=null){
900
                // add new derivation event to target and copy the event parameters of the old one
901
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
902
                derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
903
                derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
904
                derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
905
                derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
906
                derivedFromNewOriginalEvent.setType(eventToRemove.getType());
907
                to.addDerivationEvent(derivedFromNewOriginalEvent);
908
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
909
            }
910

    
911
            saveOrUpdate(from);
912
            saveOrUpdate(to);
913
            result.setStatus(Status.OK);
914
            result.addUpdatedObject(from);
915
            result.addUpdatedObject(to);
916
        } else {
917
            result.setStatus(Status.ERROR);
918
        }
919
        return result;
920
    }
921

    
922
    @Override
923
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
924
        // potential fields that are not persisted cascadingly
925
        /*
926
         * SOOB
927
        -DescriptionBase
928
        -determinations
929
        --modifier TERM
930
        -kindOfUnit TERM
931
        -lifeStage TERM
932
        -sex TERM
933

    
934
        FieldUnit
935
        -GatheringEvent
936
        --Country TERM
937
        --CollectingAreas TERM
938

    
939
        DerivedUnit
940
        -collection
941
        --institute
942
        ---types TERM
943
        -preservationMethod
944
        --medium TERM
945
        -storedUnder CDM TaxonNameBase
946
         */
947

    
948
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
949

    
950
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
951

    
952
        // FieldUnit
953
        if (specimen.isInstanceOf(FieldUnit.class)) {
954
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
955
        }
956
        // DerivedUnit
957
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
958
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
959
            if (derivedUnit.getDerivedFrom() != null) {
960
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
961
                for (FieldUnit fieldUnit : fieldUnits) {
962
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
963
                }
964
            }
965
        }
966
        return nonCascadedCdmEntities;
967
    }
968

    
969
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
970
        // get non cascaded element on SpecimenOrObservationBase level
971
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
972

    
973
        // get FieldUnit specific elements
974
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
975
        if (gatheringEvent != null) {
976
            // country
977
            if (gatheringEvent.getCountry() != null) {
978
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
979
            }
980
            // collecting areas
981
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
982
                nonCascadedCdmEntities.add(namedArea);
983
            }
984
        }
985
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
986
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
987
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
988
            }
989
        }
990
        return nonCascadedCdmEntities;
991
    }
992

    
993
    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
994
        // get non cascaded element on SpecimenOrObservationBase level
995
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
996

    
997
        // get DerivedUnit specific elements
998
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
999
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1000
                nonCascadedCdmEntities.add(type);
1001
            }
1002
        }
1003
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1004
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1005
        }
1006
        if (derivedUnit.getStoredUnder() != null) {
1007
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1008
        }
1009
        return nonCascadedCdmEntities;
1010
    }
1011

    
1012
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1013
            SpecimenOrObservationBase<?> specimen) {
1014
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1015
        // scan SpecimenOrObservationBase
1016
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1017
            // modifier
1018
            if (determinationEvent.getModifier() != null) {
1019
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
1020
            }
1021
        }
1022
        // kindOfUnit
1023
        if (specimen.getKindOfUnit() != null) {
1024
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1025
        }
1026
        // lifeStage
1027
        if (specimen.getLifeStage() != null) {
1028
            nonCascadedCdmEntities.add(specimen.getLifeStage());
1029
        }
1030
        // sex
1031
        if (specimen.getSex() != null) {
1032
            nonCascadedCdmEntities.add(specimen.getSex());
1033
        }
1034
        return nonCascadedCdmEntities;
1035
    }
1036

    
1037
    @Override
1038
    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
1039
        DeleteResult deleteResult = new DeleteResult();
1040
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1041

    
1042
        // check elements found by super method
1043
        Set<CdmBase> relatedObjects = super.isDeletable(specimen, config).getRelatedObjects();
1044
        for (CdmBase cdmBase : relatedObjects) {
1045
            // check for type designation
1046
            if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
1047
                deleteResult.setAbort();
1048
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen."));
1049
                deleteResult.addRelatedObject(cdmBase);
1050
                break;
1051
            }
1052
            // check for IndividualsAssociations
1053
            else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
1054
                deleteResult.setAbort();
1055
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated via IndividualsAssociations"));
1056
                deleteResult.addRelatedObject(cdmBase);
1057
                break;
1058
            }
1059
            // check for specimen/taxon description
1060
            else if((cdmBase.isInstanceOf(SpecimenDescription.class) || cdmBase.isInstanceOf(TaxonDescription.class))
1061
                    && !specimenDeleteConfigurator.isDeleteFromDescription()){
1062
                deleteResult.setAbort();
1063
                deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still used in a Description."));
1064
                deleteResult.addRelatedObject(cdmBase);
1065
                break;
1066
            }
1067
            // check for children and parents (derivation events)
1068
            else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1069
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1070
                // check if derivation event is empty
1071
                if (!derivationEvent.getDerivatives().isEmpty()) {
1072
                    if (derivationEvent.getDerivatives().size() == 1 && derivationEvent.getDerivatives().contains(specimen)) {
1073
                        //if it is the parent event with only one derivate then the specimen is still deletable
1074
                        continue;
1075
                    }
1076
                    else if(!specimenDeleteConfigurator.isDeleteChildren()){
1077
                        //if not and children should not be deleted then it is undeletable
1078
                        deleteResult.setAbort();
1079
                        deleteResult.addException(new ReferencedObjectUndeletableException("Derivative still has child derivatives."));
1080
                        deleteResult.addRelatedObject(cdmBase);
1081
                        break;
1082
                    }
1083
                    else{
1084
                        // check all children if they can be deleted
1085
                        Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1086
                        DeleteResult childResult = new DeleteResult();
1087
                        for (DerivedUnit derivedUnit : derivatives) {
1088
                            childResult.includeResult(isDeletable(derivedUnit, specimenDeleteConfigurator));
1089
                        }
1090
                        if (!childResult.isOk()) {
1091
                            deleteResult.setAbort();
1092
                            deleteResult.includeResult(childResult);
1093
                            deleteResult.addRelatedObject(cdmBase);
1094
                            break;
1095
                        }
1096
                    }
1097
                }
1098
            }
1099
            // check for amplification
1100
            else if (cdmBase.isInstanceOf(AmplificationResult.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1101
                deleteResult.setAbort();
1102
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1103
                deleteResult.addRelatedObject(cdmBase);
1104
                break;
1105
            }
1106
            // check for sequence
1107
            else if (cdmBase.isInstanceOf(Sequence.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1108
                deleteResult.setAbort();
1109
                deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1110
                deleteResult.addRelatedObject(cdmBase);
1111
                break;
1112
            }
1113
        }
1114
        if (deleteResult.isOk()) {
1115
            //add all related object if deletion is OK so they can be handled by the delete() method
1116
            deleteResult.addRelatedObjects(relatedObjects);
1117
        }
1118
        return deleteResult;
1119
    }
1120

    
1121
    @Override
1122
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1123
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1124

    
1125
        if (config.isDeleteChildren()) {
1126
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1127
            //clone to avoid concurrent modification
1128
            //can happen if the child is deleted and deleted its own derivedFrom event
1129
            Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
1130
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1131
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1132
                for (DerivedUnit derivedUnit : derivatives) {
1133
                    delete(derivedUnit, config);
1134
                }
1135
            }
1136
        }
1137

    
1138
        DeleteResult deleteResult = isDeletable(specimen, config);
1139
        if (!deleteResult.isOk()) {
1140
            return deleteResult;
1141
        }
1142

    
1143
        // check related objects
1144
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1145

    
1146
        for (CdmBase relatedObject : relatedObjects) {
1147
            // check for TypeDesignations
1148
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1149
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1150
                designation.setTypeSpecimen(null);
1151
                Set<TaxonNameBase> typifiedNames = designation.getTypifiedNames();
1152
                for (TaxonNameBase taxonNameBase : typifiedNames) {
1153
                    taxonNameBase.removeTypeDesignation(designation);
1154
                }
1155
            }
1156
            // delete IndividualsAssociation
1157
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1158
                IndividualsAssociation assciation = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1159
                assciation.setAssociatedSpecimenOrObservation(null);
1160
                assciation.getInDescription().removeElement(assciation);
1161
            }
1162
            // check for taxon description
1163
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1164
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1165
                taxonDescription.setDescribedSpecimenOrObservation(null);
1166
            }
1167
            // check for specimen description
1168
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1169
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1170
                // check if specimen is "described" specimen
1171
                if (specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)) {
1172
                    specimenDescription.setDescribedSpecimenOrObservation(null);
1173
                }
1174
                // check if description is a description of the given specimen
1175
                if (specimen.getDescriptions().contains(specimenDescription)) {
1176
                    specimen.removeDescription(specimenDescription);
1177
                }
1178
            }
1179
            // check for amplification
1180
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1181
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1182
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1183
            }
1184
            // check for sequence
1185
            if (relatedObject.isInstanceOf(Sequence.class)) {
1186
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1187
                sequence.getDnaSample().removeSequence(sequence);
1188
            }
1189
            // check for children and parents (derivation events)
1190
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1191
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1192
                // parent derivation event (derivedFrom)
1193
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1194
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1195
                    if (derivationEvent.getDerivatives().isEmpty()) {
1196
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1197
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1198
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1199
                            deleteResult.addUpdatedObject(specimenOrObservationBase);
1200
                        }
1201
                    }
1202
                }
1203
                else{
1204
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1205
                }
1206
            }
1207
        }
1208

    
1209
        deleteResult.includeResult(delete(specimen));
1210
        return deleteResult;
1211
    }
1212

    
1213
    @Override
1214
    public DeleteResult deleteSingleRead(SingleRead singleRead, Sequence sequence){
1215
        DeleteResult deleteResult = new DeleteResult();
1216
        singleRead = HibernateProxyHelper.deproxy(singleRead, SingleRead.class);
1217
        //delete from amplification result
1218
        if(singleRead.getAmplificationResult()!=null){
1219
            singleRead.getAmplificationResult().removeSingleRead(singleRead);
1220
        }
1221
        //delete from sequence
1222
        sequence.removeSingleRead(singleRead);
1223
        deleteResult.addUpdatedObject(sequence);
1224
        deleteResult.setStatus(Status.OK);
1225
        return deleteResult;
1226
    }
1227

    
1228
    @Override
1229
    @Transactional(readOnly = false)
1230
    public DeleteResult deleteSingleRead(UUID singleReadUuid, UUID sequenceUuid){
1231
        SingleRead singleRead = null;
1232
        Sequence sequence = CdmBase.deproxy(sequenceService.load(sequenceUuid), Sequence.class);
1233
        for(SingleRead sr : sequence.getSingleReads()) {
1234
            if(sr.getUuid().equals(singleReadUuid)) {
1235
                singleRead = sr;
1236
                break;
1237
            }
1238
        }
1239
        return deleteSingleRead(singleRead, sequence);
1240
    }
1241

    
1242
    @Override
1243
    public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {
1244
        DeleteResult deleteResult = new DeleteResult();
1245
        String deleteMolecularNotAllowed = "Deleting molecular data is not allowed in config";
1246
        if (from.isInstanceOf(Sequence.class)) {
1247
            if (!config.isDeleteMolecularData()) {
1248
                deleteResult.setAbort();
1249
                deleteResult.addException(new ReferencedObjectUndeletableException(deleteMolecularNotAllowed));
1250
                return deleteResult;
1251
            }
1252
            Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);
1253
            DnaSample dnaSample = sequence.getDnaSample();
1254
            dnaSample.removeSequence(sequence);
1255
            deleteResult = sequenceService.delete(sequence);
1256
            deleteResult.addUpdatedObject(dnaSample);
1257
        }
1258
        else if(from instanceof SingleRead){
1259
            SingleRead singleRead = (SingleRead)from;
1260
            //delete from amplification result
1261
            if(singleRead.getAmplificationResult()!=null){
1262
                singleRead.getAmplificationResult().removeSingleRead(singleRead);
1263
            }
1264
            deleteResult.setAbort();
1265
            deleteResult.addException(new ReferencedObjectUndeletableException("Deleted ONLY from amplification. "
1266
                    + "Single read may still be attached to a consensus sequence."));
1267
        }
1268
        else if(from.isInstanceOf(SpecimenOrObservationBase.class))  {
1269
            deleteResult = delete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config);
1270
        }
1271
        return deleteResult;
1272
    }
1273

    
1274
    @Override
1275
    @Transactional(readOnly = false)
1276
    public DeleteResult deleteDerivateHierarchy(UUID fromUuid, SpecimenDeleteConfigurator config) {
1277
        return deleteDerivateHierarchy(dao.load(fromUuid),config);
1278
    }
1279

    
1280
//    private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){
1281
    // Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();
1282
    // for (DerivationEvent derivationEvent : derivationEvents) {
1283
    // Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1284
    // for (DerivedUnit derivedUnit : derivatives) {
1285
    // DeleteResult deleteResult = deepDelete(derivedUnit, config);
1286
    // if(!deleteResult.isOk()){
1287
    // return deleteResult;
1288
    // }
1289
    // }
1290
    // }
1291
    // return delete(entity, config);
1292
    // }
1293

    
1294
    @Override
1295
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1296
        return dao.listIndividualsAssociations(specimen, null, null, null, null);
1297
    }
1298

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

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

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

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

    
1333

    
1334
    @Override
1335
    public Pager<SpecimenOrObservationBase> findByTitle(
1336
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1337
        if (config instanceof FindOccurrencesConfigurator) {
1338
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1339
            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1340
            Taxon taxon = null;
1341
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1342
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1343
                if(taxonBase.isInstanceOf(Taxon.class)){
1344
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1345
                }
1346
            }
1347
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1348
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1349
                    occurrenceConfig.getSpecimenType(), taxon, occurrenceConfig.getMatchMode(), null, null,
1350
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1351
            // indirectly associated specimens
1352
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1353
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1354
                for (SpecimenOrObservationBase specimen : occurrences) {
1355
                    List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1356
                    for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1357
                        if(!occurrences.contains(specimenOrObservationBase)){
1358
                            indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1359
                        }
1360
                    }
1361
                }
1362
                occurrences = indirectlyAssociatedOccurrences;
1363
            }
1364

    
1365
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1366
        }
1367
        return super.findByTitle(config);
1368
    }
1369

    
1370
    @Override
1371
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1372
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1373
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1374
        if(fieldUnits.isEmpty()){
1375
            allHierarchyDerivatives.add(specimen);
1376
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1377
        }
1378
        else{
1379
            for (FieldUnit fieldUnit : fieldUnits) {
1380
                allHierarchyDerivatives.add(fieldUnit);
1381
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1382
            }
1383
        }
1384
        return allHierarchyDerivatives;
1385
    }
1386

    
1387
    @Override
1388
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1389
        List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1390
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1391
        for (DerivationEvent derivationEvent : derivationEvents) {
1392
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1393
            for (DerivedUnit derivedUnit : derivatives) {
1394
                childDerivate.add(derivedUnit);
1395
                childDerivate.addAll(getAllChildDerivatives(derivedUnit));
1396
            }
1397
        }
1398
        return childDerivate;
1399
    }
1400

    
1401
    @Override
1402
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1403
        if (config instanceof FindOccurrencesConfigurator) {
1404
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1405
            Taxon taxon = null;
1406
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1407
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1408
                if(taxonBase.isInstanceOf(Taxon.class)){
1409
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1410
                }
1411
            }
1412
            // indirectly associated specimens
1413
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1414
                return findByTitle(config).getRecords().size();
1415
            }
1416
            return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
1417
                    occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon,
1418
                    occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
1419
                    occurrenceConfig.getPropertyPaths());
1420
        }
1421
        return super.countByTitle(config);
1422
    }
1423

    
1424
}
(75-75/92)