Project

General

Profile

Download (71.7 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!=null?from.getUuid():null, 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 = null;
876
        if(specimenFromUuid!=null){
877
            from = load(specimenFromUuid);
878
        }
879
        SpecimenOrObservationBase<?> to = load(specimenToUuid);
880
        DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
881

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

    
922
            if(from!=null){
923
                saveOrUpdate(from);
924
            }
925
            saveOrUpdate(to);
926
            result.setStatus(Status.OK);
927
            result.addUpdatedObject(from);
928
            result.addUpdatedObject(to);
929
        } else {
930
            result.setStatus(Status.ERROR);
931
        }
932
        return result;
933
    }
934

    
935
    @Override
936
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
937
        // potential fields that are not persisted cascadingly
938
        /*
939
         * SOOB
940
        -DescriptionBase
941
        -determinations
942
        --modifier TERM
943
        -kindOfUnit TERM
944
        -lifeStage TERM
945
        -sex TERM
946

    
947
        FieldUnit
948
        -GatheringEvent
949
        --Country TERM
950
        --CollectingAreas TERM
951

    
952
        DerivedUnit
953
        -collection
954
        --institute
955
        ---types TERM
956
        -preservationMethod
957
        --medium TERM
958
        -storedUnder CDM TaxonNameBase
959
         */
960

    
961
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
962

    
963
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
964

    
965
        // FieldUnit
966
        if (specimen.isInstanceOf(FieldUnit.class)) {
967
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
968
        }
969
        // DerivedUnit
970
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
971
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
972
            if (derivedUnit.getDerivedFrom() != null) {
973
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
974
                for (FieldUnit fieldUnit : fieldUnits) {
975
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
976
                }
977
            }
978
        }
979
        return nonCascadedCdmEntities;
980
    }
981

    
982
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
983
        // get non cascaded element on SpecimenOrObservationBase level
984
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
985

    
986
        // get FieldUnit specific elements
987
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
988
        if (gatheringEvent != null) {
989
            // country
990
            if (gatheringEvent.getCountry() != null) {
991
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
992
            }
993
            // collecting areas
994
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
995
                nonCascadedCdmEntities.add(namedArea);
996
            }
997
        }
998
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
999
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1000
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
1001
            }
1002
        }
1003
        return nonCascadedCdmEntities;
1004
    }
1005

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

    
1010
        // get DerivedUnit specific elements
1011
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
1012
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1013
                nonCascadedCdmEntities.add(type);
1014
            }
1015
        }
1016
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1017
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1018
        }
1019
        if (derivedUnit.getStoredUnder() != null) {
1020
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1021
        }
1022
        return nonCascadedCdmEntities;
1023
    }
1024

    
1025
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1026
            SpecimenOrObservationBase<?> specimen) {
1027
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1028
        // scan SpecimenOrObservationBase
1029
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1030
            // modifier
1031
            if (determinationEvent.getModifier() != null) {
1032
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
1033
            }
1034
        }
1035
        // kindOfUnit
1036
        if (specimen.getKindOfUnit() != null) {
1037
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1038
        }
1039
        // lifeStage
1040
        if (specimen.getLifeStage() != null) {
1041
            nonCascadedCdmEntities.add(specimen.getLifeStage());
1042
        }
1043
        // sex
1044
        if (specimen.getSex() != null) {
1045
            nonCascadedCdmEntities.add(specimen.getSex());
1046
        }
1047
        return nonCascadedCdmEntities;
1048
    }
1049

    
1050
    @Override
1051
    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
1052
        DeleteResult deleteResult = new DeleteResult();
1053
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1054

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

    
1134
    @Override
1135
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1136
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1137

    
1138
        if (config.isDeleteChildren()) {
1139
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1140
            //clone to avoid concurrent modification
1141
            //can happen if the child is deleted and deleted its own derivedFrom event
1142
            Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
1143
            for (DerivationEvent derivationEvent : derivationEventsClone) {
1144
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1145
                for (DerivedUnit derivedUnit : derivatives) {
1146
                    delete(derivedUnit, config);
1147
                }
1148
            }
1149
        }
1150

    
1151
        DeleteResult deleteResult = isDeletable(specimen, config);
1152
        if (!deleteResult.isOk()) {
1153
            return deleteResult;
1154
        }
1155

    
1156
        // check related objects
1157
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1158

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

    
1226
        deleteResult.includeResult(delete(specimen));
1227
        return deleteResult;
1228
    }
1229

    
1230
    @Override
1231
    public DeleteResult deleteSingleRead(SingleRead singleRead, Sequence sequence){
1232
        DeleteResult deleteResult = new DeleteResult();
1233
        singleRead = HibernateProxyHelper.deproxy(singleRead, SingleRead.class);
1234
        //delete from amplification result
1235
        if(singleRead.getAmplificationResult()!=null){
1236
            singleRead.getAmplificationResult().removeSingleRead(singleRead);
1237
        }
1238
        //delete from sequence
1239
        sequence.removeSingleRead(singleRead);
1240
        deleteResult.addUpdatedObject(sequence);
1241
        deleteResult.setStatus(Status.OK);
1242
        return deleteResult;
1243
    }
1244

    
1245
    @Override
1246
    @Transactional(readOnly = false)
1247
    public DeleteResult deleteSingleRead(UUID singleReadUuid, UUID sequenceUuid){
1248
        SingleRead singleRead = null;
1249
        Sequence sequence = CdmBase.deproxy(sequenceService.load(sequenceUuid), Sequence.class);
1250
        for(SingleRead sr : sequence.getSingleReads()) {
1251
            if(sr.getUuid().equals(singleReadUuid)) {
1252
                singleRead = sr;
1253
                break;
1254
            }
1255
        }
1256
        return deleteSingleRead(singleRead, sequence);
1257
    }
1258

    
1259
    @Override
1260
    public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {
1261
        DeleteResult deleteResult = new DeleteResult();
1262
        String deleteMolecularNotAllowed = "Deleting molecular data is not allowed in config";
1263
        if (from.isInstanceOf(Sequence.class)) {
1264
            if (!config.isDeleteMolecularData()) {
1265
                deleteResult.setAbort();
1266
                deleteResult.addException(new ReferencedObjectUndeletableException(deleteMolecularNotAllowed));
1267
                return deleteResult;
1268
            }
1269
            Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);
1270
            DnaSample dnaSample = sequence.getDnaSample();
1271
            dnaSample.removeSequence(sequence);
1272
            deleteResult = sequenceService.delete(sequence);
1273
            deleteResult.addUpdatedObject(dnaSample);
1274
        }
1275
        else if(from instanceof SingleRead){
1276
            SingleRead singleRead = (SingleRead)from;
1277
            //delete from amplification result
1278
            if(singleRead.getAmplificationResult()!=null){
1279
                singleRead.getAmplificationResult().removeSingleRead(singleRead);
1280
            }
1281
            deleteResult.setAbort();
1282
            deleteResult.addException(new ReferencedObjectUndeletableException("Deleted ONLY from amplification. "
1283
                    + "Single read may still be attached to a consensus sequence."));
1284
        }
1285
        else if(from.isInstanceOf(SpecimenOrObservationBase.class))  {
1286
            deleteResult = delete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config);
1287
        }
1288
        return deleteResult;
1289
    }
1290

    
1291
    @Override
1292
    @Transactional(readOnly = false)
1293
    public DeleteResult deleteDerivateHierarchy(UUID fromUuid, SpecimenDeleteConfigurator config) {
1294
        return deleteDerivateHierarchy(dao.load(fromUuid),config);
1295
    }
1296

    
1297
//    private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){
1298
    // Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();
1299
    // for (DerivationEvent derivationEvent : derivationEvents) {
1300
    // Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1301
    // for (DerivedUnit derivedUnit : derivatives) {
1302
    // DeleteResult deleteResult = deepDelete(derivedUnit, config);
1303
    // if(!deleteResult.isOk()){
1304
    // return deleteResult;
1305
    // }
1306
    // }
1307
    // }
1308
    // return delete(entity, config);
1309
    // }
1310

    
1311
    @Override
1312
    public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1313
        return dao.listIndividualsAssociations(specimen, null, null, null, null);
1314
    }
1315

    
1316
    /**
1317
     * {@inheritDoc}
1318
     */
1319
    @Override
1320
    public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1321
            List<OrderHint> orderHints, List<String> propertyPaths) {
1322
        Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1323
        for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1324
            if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1325
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1326
                associatedTaxa.add(taxonDescription.getTaxon());
1327
            }
1328
        }
1329
        return associatedTaxa;
1330
    }
1331

    
1332
    @Override
1333
    public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,
1334
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1335
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1336
    }
1337

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

    
1345
    @Override
1346
    @Deprecated //this is not a service layer task so it may be removed in future versions
1347
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1348
        if (specimen != null) {
1349
            return specimen.characterData();
1350
        }else{
1351
            return new ArrayList<DescriptionElementBase>();
1352
        }
1353
    }
1354

    
1355
    @Override
1356
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1357
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1358
        if (specimen != null) {
1359
            return getCharacterDataForSpecimen(specimen);
1360
        }
1361
        else{
1362
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1363
        }
1364
    }
1365

    
1366

    
1367
    @Override
1368
    public Pager<SpecimenOrObservationBase> findByTitle(
1369
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1370
        if (config instanceof FindOccurrencesConfigurator) {
1371
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1372
            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1373
            Taxon taxon = null;
1374
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1375
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1376
                if(taxonBase.isInstanceOf(Taxon.class)){
1377
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1378
                }
1379
            }
1380
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1381
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1382
                    occurrenceConfig.getSpecimenType(), taxon, occurrenceConfig.getMatchMode(), null, null,
1383
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1384
            // indirectly associated specimens
1385
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1386
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1387
                for (SpecimenOrObservationBase specimen : occurrences) {
1388
                    List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1389
                    for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1390
                        if(!occurrences.contains(specimenOrObservationBase)){
1391
                            indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1392
                        }
1393
                    }
1394
                }
1395
                occurrences = indirectlyAssociatedOccurrences;
1396
            }
1397

    
1398
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1399
        }
1400
        return super.findByTitle(config);
1401
    }
1402

    
1403
    @Override
1404
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1405
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1406
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1407
        if(fieldUnits.isEmpty()){
1408
            allHierarchyDerivatives.add(specimen);
1409
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1410
        }
1411
        else{
1412
            for (FieldUnit fieldUnit : fieldUnits) {
1413
                allHierarchyDerivatives.add(fieldUnit);
1414
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1415
            }
1416
        }
1417
        return allHierarchyDerivatives;
1418
    }
1419

    
1420
    @Override
1421
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1422
        List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1423
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1424
        for (DerivationEvent derivationEvent : derivationEvents) {
1425
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1426
            for (DerivedUnit derivedUnit : derivatives) {
1427
                childDerivate.add(derivedUnit);
1428
                childDerivate.addAll(getAllChildDerivatives(derivedUnit));
1429
            }
1430
        }
1431
        return childDerivate;
1432
    }
1433

    
1434
    @Override
1435
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1436
        if (config instanceof FindOccurrencesConfigurator) {
1437
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1438
            Taxon taxon = null;
1439
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1440
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1441
                if(taxonBase.isInstanceOf(Taxon.class)){
1442
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1443
                }
1444
            }
1445
            // indirectly associated specimens
1446
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1447
                return findByTitle(config).getRecords().size();
1448
            }
1449
            return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
1450
                    occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon,
1451
                    occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
1452
                    occurrenceConfig.getPropertyPaths());
1453
        }
1454
        return super.countByTitle(config);
1455
    }
1456

    
1457
}
(76-76/95)