Project

General

Profile

Download (68 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<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber,	List<OrderHint> orderHints, List<String> propertyPaths) {
231
        Integer numberOfResults = dao.count(type, determinedAs);
232
        List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
233
        pageNumber = pageNumber == null ? 0 : pageNumber;
234
        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
235
            Integer start = pageSize == null ? 0 : pageSize * pageNumber;
236
            results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
237
        }
238
        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
239
    }
240

    
241
    @Override
242
    public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
243
        return dao.getDerivedUnitUuidAndTitleCache();
244
    }
245

    
246
    @Override
247
    public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
248
        return dao.getFieldUnitUuidAndTitleCache();
249
    }
250

    
251
    @Override
252
    public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
253
        derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
254
        DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
255
        config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
256
        DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
257
        beanInitializer.initialize(derivedUnitFacade, propertyPaths);
258
        return derivedUnitFacade;
259
    }
260

    
261
    @Override
262
    public List<DerivedUnitFacade> listDerivedUnitFacades(
263
            DescriptionBase description, List<String> propertyPaths) {
264

    
265
        List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
266
        IndividualsAssociation tempIndividualsAssociation;
267
        SpecimenOrObservationBase tempSpecimenOrObservationBase;
268
        List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
269
        for (DescriptionElementBase element : elements) {
270
            if (element.isInstanceOf(IndividualsAssociation.class)) {
271
                tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
272
                if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
273
                    tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
274
                    if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
275
                        try {
276
                            derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
277
                        } catch (DerivedUnitFacadeNotSupportedException e) {
278
                            logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
279
                        }
280
                    }
281
                }
282

    
283
            }
284
        }
285

    
286
        beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
287

    
288
        return derivedUnitFacadeList;
289
    }
290

    
291

    
292
    @Override
293
    public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
294
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
295

    
296
        return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
297
    }
298

    
299
    @Override
300
    public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
301
        return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
302
    }
303

    
304
    @Override
305
    public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
306
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
307
            List<String> propertyPaths) {
308

    
309
        if (!getSession().contains(associatedTaxon)) {
310
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
311
        }
312

    
313
        // gather the IDs of all relevant field units
314
        Set<Integer> fieldUnitIds = new HashSet<Integer>();
315
        List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
316
        for (SpecimenOrObservationBase<?> specimen : records) {
317
            for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
318
                fieldUnitIds.add(fieldUnit.getId());
319
            }
320
        }
321
        //dao.listByIds() does the paging of the field units. Passing the field units directly to the Pager would not work
322
        List<SpecimenOrObservationBase> fieldUnits = dao.listByIds(fieldUnitIds, pageSize, pageNumber, orderHints, propertyPaths);
323
        return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitIds.size(), pageSize, fieldUnits);
324
    }
325

    
326
    @Override
327
    public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
328

    
329
        if (!getSession().contains(fieldUnit)) {
330
            fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
331
        }
332
        TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
333

    
334
        FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
335

    
336
        if (fieldUnit.getGatheringEvent() != null) {
337
            GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
338
            // Country
339
            NamedArea country = gatheringEvent.getCountry();
340
            fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
341
            // Collection
342
            AgentBase collector = gatheringEvent.getCollector();
343
            String fieldNumber = fieldUnit.getFieldNumber();
344
            String collectionString = "";
345
            if (collector != null || fieldNumber != null) {
346
                collectionString += collector != null ? collector : "";
347
                if (!collectionString.isEmpty()) {
348
                    collectionString += " ";
349
                }
350
                collectionString += (fieldNumber != null ? fieldNumber : "");
351
                collectionString.trim();
352
            }
353
            fieldUnitDTO.setCollection(collectionString);
354
            // Date
355
            Partial gatheringDate = gatheringEvent.getGatheringDate();
356
            String dateString = null;
357
            if (gatheringDate != null) {
358
                gatheringDate.toString();
359
            }
360
            else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
361
                dateString = gatheringEvent.getTimeperiod().getFreeText();
362
            }
363
            fieldUnitDTO.setDate(dateString);
364
        }
365

    
366
        // Taxon Name
367
        fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
368

    
369
        // Herbaria map
370
        Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
371
        // List of accession numbers for citation
372
        List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
373

    
374
        // assemble preserved specimen DTOs
375
        Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
376
        for (DerivationEvent derivationEvent : derivationEvents) {
377
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
378
            for (DerivedUnit derivedUnit : derivatives) {
379
                // collect accession numbers for citation
380
                String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
381
                if (mostSignificantIdentifier != null) {
382
                    preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
383
                }
384
                // collect collections for herbaria column
385
                if (derivedUnit.getCollection() != null) {
386
                    Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
387
                    if (herbariumCount == null) {
388
                        herbariumCount = 0;
389
                    }
390
                    collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
391
                }
392
                if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
393
                    PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
394
                    fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
395
                    fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
396
                    fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
397
                    fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
398
                    fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
399
                }
400
            }
401
        }
402
        // assemble derivate data DTO
403
        assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
404

    
405
        // assemble citation
406
        String citation = fieldUnit.getTitleCache();
407
        if (!preservedSpecimenAccessionNumbers.isEmpty()) {
408
            citation += " (";
409
            for (String accessionNumber : preservedSpecimenAccessionNumbers) {
410
                if (!accessionNumber.isEmpty()) {
411
                    citation += accessionNumber + SEPARATOR_STRING;
412
                }
413
            }
414
            citation = removeTail(citation, SEPARATOR_STRING);
415
            citation += ")";
416
        }
417
        fieldUnitDTO.setCitation(citation);
418

    
419
        // assemble herbaria string
420
        String herbariaString = "";
421
        for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
422
            eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
423
            if (collection.getCode() != null) {
424
                herbariaString += collection.getCode();
425
            }
426
            if (e.getValue() > 1) {
427
                herbariaString += "(" + e.getValue() + ")";
428
            }
429
            herbariaString += SEPARATOR_STRING;
430
        }
431
        herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
432
        fieldUnitDTO.setHerbarium(herbariaString);
433

    
434
        return fieldUnitDTO;
435
    }
436

    
437
    @Override
438
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
439
        return assemblePreservedSpecimenDTO(derivedUnit, null);
440
    }
441

    
442
    @Override
443
    public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
444
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
445
            return derivedUnit.getAccessionNumber();
446
        }
447
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
448
            return derivedUnit.getBarcode();
449
        }
450
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
451
            return derivedUnit.getCatalogNumber();
452
        }
453
        return null;
454
    }
455

    
456
    public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
457
        if (!getSession().contains(derivedUnit)) {
458
            derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
459
        }
460
        PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
461

    
462
        // check identifiers in priority order accNo>barCode>catalogNumber
463
        if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
464
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
465
        }
466
        else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
467
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
468
        }
469
        else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
470
            preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
471
        }
472
        preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
473

    
474
        // citation
475
        Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
476
        if (fieldUnits.size() == 1) {
477
            preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
478
        }
479
        else{
480
            preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
481
        }
482

    
483
        // character state data
484
        Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
485
        if (!characterDataForSpecimen.isEmpty()) {
486
            if (fieldUnitDTO != null) {
487
                fieldUnitDTO.setHasCharacterData(true);
488
            }
489
        }
490
        for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
491
            String character = descriptionElementBase.getFeature().getLabel();
492
            ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
493
            if (descriptionElementBase instanceof QuantitativeData) {
494
                QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
495
                DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
496
                String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
497
                preservedSpecimenDTO.addCharacterData(character, state);
498
            }
499
            else if(descriptionElementBase instanceof CategoricalData){
500
                CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
501
                DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
502
                String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
503
                preservedSpecimenDTO.addCharacterData(character, state);
504
            }
505
        }
506
        // check type designations
507
        Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
508
        for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
509
            if (fieldUnitDTO != null) {
510
                fieldUnitDTO.setHasType(true);
511
            }
512
            TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
513
            if (typeStatus != null) {
514
                List<String> typedTaxaNames = new ArrayList<String>();
515
                String label = typeStatus.getLabel();
516
                Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
517
                for (TaxonNameBase taxonNameBase : typifiedNames) {
518
                    typedTaxaNames.add(taxonNameBase.getFullTitleCache());
519
                }
520
                preservedSpecimenDTO.addTypes(label, typedTaxaNames);
521
            }
522
        }
523

    
524
        // individuals associations
525
        Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
526
        for (IndividualsAssociation individualsAssociation : individualsAssociations) {
527
            if (individualsAssociation.getInDescription() != null) {
528
                if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
529
                    TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
530
                    Taxon taxon = taxonDescription.getTaxon();
531
                    if (taxon != null && taxon.getName() != null) {
532
                        preservedSpecimenDTO.addAssociatedTaxon(taxon.getName().getTitleCache());
533
                    }
534
                }
535
            }
536
        }
537
        // assemble sub derivates
538
        preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
539
        return preservedSpecimenDTO;
540
    }
541

    
542
    private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
543
        DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
544
        Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
545
        for (DerivedUnit childDerivate : childDerivates) {
546
            // assemble molecular data
547
            //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
548
            if (childDerivate.isInstanceOf(DnaSample.class)) {
549
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
550
                    // TODO implement TissueSample assembly for web service
551
                }
552
                if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
553

    
554
                    DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
555
                    if (!dna.getSequences().isEmpty()) {
556
                        derivateDTO.setHasDna(true);
557
                    }
558
                    for (Sequence sequence : dna.getSequences()) {
559
                        URI boldUri = null;
560
                        try {
561
                            boldUri = sequence.getBoldUri();
562
                        } catch (URISyntaxException e1) {
563
                            logger.error("Could not create BOLD URI", e1);
564
                        }
565
                        final DefinedTerm dnaMarker = sequence.getDnaMarker();
566
                        MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
567

    
568
                        //contig file
569
                        ContigFile contigFile = null;
570
                        if (sequence.getContigFile() != null) {
571
                            MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
572
                            if (contigMediaRepresentationPart != null) {
573
                                contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
574
                            }
575
                        }
576
                        if(contigFile==null){
577
                            contigFile = molecularData.addContigFile(null, "[no contig]");
578
                        }
579
                        // primer files
580
                        if (sequence.getSingleReads() != null) {
581
                            int readCount = 1;
582
                            for (SingleRead singleRead : sequence.getSingleReads()) {
583
                                MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
584
                                if (pherogramMediaRepresentationPart != null) {
585
                                    contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
586
                                }
587
                            }
588
                        }
589
                    }
590
                }
591
            }
592
            // assemble media data
593
            else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
594
                MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
595

    
596
                String mediaUriString = getMediaUriString(media);
597
                if (media.getKindOfUnit() != null) {
598
                    // specimen scan
599
                    if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
600
                        derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
601
                        derivateDTO.setHasSpecimenScan(true);
602
                        String imageLinkText = "scan";
603
                        if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
604
                            imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
605
                        }
606
                        derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
607
                    }
608
                    // detail image
609
                    else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
610
                        derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
611
                        derivateDTO.setHasDetailImage(true);
612
                        String motif = "";
613
                        if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
614
                            motif = media.getMediaSpecimen().getTitle().getText();
615
                        }
616
                        derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
617
                    }
618
                }
619
            }
620
        }
621
        return derivateDataDTO;
622
    }
623

    
624
    private String removeTail(String string, final String tail) {
625
        if (string.endsWith(tail)) {
626
            string = string.substring(0, string.length() - tail.length());
627
        }
628
        return string;
629
    }
630

    
631
    private String getMediaUriString(MediaSpecimen mediaSpecimen) {
632
        String mediaUri = null;
633
        Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
634
        if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
635
            Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
636
            if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
637
                MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
638
                if (part.getUri() != null) {
639
                    mediaUri = part.getUri().toASCIIString();
640
                }
641
            }
642
        }
643
        return mediaUri;
644
    }
645

    
646
    private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
647
        Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
648
        for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
649
            for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
650
                derivedUnits.add(derivative);
651
                derivedUnits.addAll(getDerivedUnitsFor(derivative));
652
            }
653
        }
654
        return derivedUnits;
655
    }
656

    
657

    
658
    @SuppressWarnings("unchecked")
659
    @Override
660
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
661
            Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
662

    
663
        Set<Taxon> taxa = new HashSet<Taxon>();
664
        Set<Integer> occurrenceIds = new HashSet<Integer>();
665
        List<T> occurrences = new ArrayList<T>();
666

    
667
        // Integer limit = PagerUtils.limitFor(pageSize);
668
        // Integer start = PagerUtils.startFor(pageSize, pageNumber);
669

    
670
        if (!getSession().contains(associatedTaxon)) {
671
            associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
672
        }
673

    
674
        if (includeRelationships != null) {
675
            taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
676
        }
677

    
678
        taxa.add(associatedTaxon);
679

    
680
        for (Taxon taxon : taxa) {
681
            List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
682
            for (SpecimenOrObservationBase o : perTaxonOccurrences) {
683
                occurrenceIds.add(o.getId());
684
            }
685
        }
686
        occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
687

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

    
690
    }
691

    
692
    @Override
693
    public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
694
            String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
695

    
696
        UUID uuid = UUID.fromString(taxonUUID);
697
        Taxon tax = (Taxon) taxonService.load(uuid);
698
        // TODO REMOVE NULL STATEMENT
699
        type = null;
700
        return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
701

    
702
    }
703

    
704
    @Override
705
    public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
706
            Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
707
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
708
            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
709

    
710
        LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
711

    
712
        // --- execute search
713
        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
714

    
715
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
716
        idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
717

    
718
        // --- initialize taxa, highlight matches ....
719
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
720
        @SuppressWarnings("rawtypes")
721
        List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
722
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
723

    
724
        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
725

    
726
        return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
727
                searchResults);
728

    
729
    }
730

    
731
    private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
732
            List<Language> languages, boolean highlightFragments) {
733

    
734
        BooleanQuery finalQuery = new BooleanQuery();
735
        BooleanQuery textQuery = new BooleanQuery();
736

    
737
        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
738
        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
739

    
740
        // --- criteria
741
        luceneSearch.setCdmTypRestriction(clazz);
742
        if (queryString != null) {
743
            textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
744
            finalQuery.add(textQuery, Occur.MUST);
745
        }
746

    
747
        // --- spacial query
748
        if (bbox != null) {
749
            finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
750
        }
751

    
752
        luceneSearch.setQuery(finalQuery);
753

    
754
        // --- sorting
755
        SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false) };
756
        luceneSearch.setSortFields(sortFields);
757

    
758
        if (highlightFragments) {
759
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
760
        }
761
        return luceneSearch;
762
    }
763

    
764

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

    
770
        // FIXME: use HQL queries to increase performance
771
        SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
772
//        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
773
        Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
774

    
775
        if (specimen.isInstanceOf(FieldUnit.class)) {
776
            fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
777
        }
778
        else if(specimen.isInstanceOf(DerivedUnit.class)){
779
            fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
780
        }
781
        return fieldUnits;
782
    }
783

    
784
    private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
785
        Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
786
        Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
787
        if (originals != null && !originals.isEmpty()) {
788
            for (SpecimenOrObservationBase<?> original : originals) {
789
                if (original.isInstanceOf(FieldUnit.class)) {
790
                    fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
791
                }
792
                else if(original.isInstanceOf(DerivedUnit.class)){
793
                    fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
794
                }
795
            }
796
        }
797
        return fieldUnits;
798
    }
799

    
800
    @Override
801
    public boolean moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
802
        // reload specimens to avoid session conflicts
803
        from = (DnaSample) load(from.getUuid());
804
        to = (DnaSample) load(to.getUuid());
805
        sequence = sequenceService.load(sequence.getUuid());
806

    
807
        if (from == null || to == null || sequence == null) {
808
            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" +
809
                    "Operation was move "+sequence+ " from "+from+" to "+to);
810
        }
811
        from.removeSequence(sequence);
812
        saveOrUpdate(from);
813
        to.addSequence(sequence);
814
        saveOrUpdate(to);
815
        return true;
816
    }
817

    
818
    @Override
819
    public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
820
        // reload specimens to avoid session conflicts
821
        from = load(from.getUuid());
822
        to = load(to.getUuid());
823
        derivate = (DerivedUnit) load(derivate.getUuid());
824

    
825
        if (from == null || to == null || derivate == null) {
826
            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" +
827
            		"Operation was move "+derivate+ " from "+from+" to "+to);
828
        }
829

    
830
        SpecimenOrObservationType derivateType = derivate.getRecordBasis();
831
        SpecimenOrObservationType toType = to.getRecordBasis();
832
        // check if type is a sub derivate type
833
        if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
834
                || derivateType==SpecimenOrObservationType.Media //moving media always works
835
                || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
836
            // remove derivation event from parent specimen of dragged object
837
            DerivationEvent eventToRemove = null;
838
            for (DerivationEvent event : from.getDerivationEvents()) {
839
                if (event.getDerivatives().contains(derivate)) {
840
                    eventToRemove = event;
841
                    break;
842
                }
843
            }
844
            from.removeDerivationEvent(eventToRemove);
845
            if(eventToRemove!=null){
846
                // add new derivation event to target and copy the event parameters of the old one
847
                DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
848
                derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
849
                derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
850
                derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
851
                derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
852
                derivedFromNewOriginalEvent.setType(eventToRemove.getType());
853
                to.addDerivationEvent(derivedFromNewOriginalEvent);
854
                derivate.setDerivedFrom(derivedFromNewOriginalEvent);
855
            }
856

    
857
            saveOrUpdate(from);
858
            saveOrUpdate(to);
859
            return true;
860
        }
861
        return false;
862
    }
863

    
864
    @Override
865
    public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
866
        // potential fields that are not persisted cascadingly
867
        /*
868
         * SOOB
869
        -DescriptionBase
870
        -determinations
871
        --modifier TERM
872
        -kindOfUnit TERM
873
        -lifeStage TERM
874
        -sex TERM
875

    
876
        FieldUnit
877
        -GatheringEvent
878
        --Country TERM
879
        --CollectingAreas TERM
880

    
881
        DerivedUnit
882
        -collection
883
        --institute
884
        ---types TERM
885
        -preservationMethod
886
        --medium TERM
887
        -storedUnder CDM TaxonNameBase
888
         */
889

    
890
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
891

    
892
        //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
893

    
894
        // FieldUnit
895
        if (specimen.isInstanceOf(FieldUnit.class)) {
896
            nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
897
        }
898
        // DerivedUnit
899
        else if (specimen.isInstanceOf(DerivedUnit.class)) {
900
            DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
901
            if (derivedUnit.getDerivedFrom() != null) {
902
                Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
903
                for (FieldUnit fieldUnit : fieldUnits) {
904
                    nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
905
                }
906
            }
907
        }
908
        return nonCascadedCdmEntities;
909
    }
910

    
911
    private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
912
        // get non cascaded element on SpecimenOrObservationBase level
913
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
914

    
915
        // get FieldUnit specific elements
916
        GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
917
        if (gatheringEvent != null) {
918
            // country
919
            if (gatheringEvent.getCountry() != null) {
920
                nonCascadedCdmEntities.add(gatheringEvent.getCountry());
921
            }
922
            // collecting areas
923
            for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
924
                nonCascadedCdmEntities.add(namedArea);
925
            }
926
        }
927
        for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
928
            for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
929
                nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
930
            }
931
        }
932
        return nonCascadedCdmEntities;
933
    }
934

    
935
    private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
936
        // get non cascaded element on SpecimenOrObservationBase level
937
        Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
938

    
939
        // get DerivedUnit specific elements
940
        if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
941
            for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
942
                nonCascadedCdmEntities.add(type);
943
            }
944
        }
945
        if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
946
            nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
947
        }
948
        if (derivedUnit.getStoredUnder() != null) {
949
            nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
950
        }
951
        return nonCascadedCdmEntities;
952
    }
953

    
954
    private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
955
            SpecimenOrObservationBase<?> specimen) {
956
        Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
957
        // scan SpecimenOrObservationBase
958
        for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
959
            // modifier
960
            if (determinationEvent.getModifier() != null) {
961
                nonCascadedCdmEntities.add(determinationEvent.getModifier());
962
            }
963
        }
964
        // kindOfUnit
965
        if (specimen.getKindOfUnit() != null) {
966
            nonCascadedCdmEntities.add(specimen.getKindOfUnit());
967
        }
968
        // lifeStage
969
        if (specimen.getLifeStage() != null) {
970
            nonCascadedCdmEntities.add(specimen.getLifeStage());
971
        }
972
        // sex
973
        if (specimen.getSex() != null) {
974
            nonCascadedCdmEntities.add(specimen.getSex());
975
        }
976
        return nonCascadedCdmEntities;
977
    }
978

    
979
    @Override
980
    public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
981
        DeleteResult deleteResult = new DeleteResult();
982
        SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
983

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

    
1063
    @Override
1064
    public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1065
        specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1066

    
1067
        if (config.isDeleteChildren()) {
1068
            Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1069
            for (DerivationEvent derivationEvent : derivationEvents) {
1070
                Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1071
                for (DerivedUnit derivedUnit : derivatives) {
1072
                    delete(derivedUnit, config);
1073
                }
1074
            }
1075
        }
1076

    
1077
        DeleteResult deleteResult = isDeletable(specimen, config);
1078
        if (!deleteResult.isOk()) {
1079
            return deleteResult;
1080
        }
1081

    
1082
        // check related objects
1083
        Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1084

    
1085
        for (CdmBase relatedObject : relatedObjects) {
1086
            // check for TypeDesignations
1087
            if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1088
                SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1089
                designation.setTypeSpecimen(null);
1090
                Set<TaxonNameBase> typifiedNames = designation.getTypifiedNames();
1091
                for (TaxonNameBase taxonNameBase : typifiedNames) {
1092
                    taxonNameBase.removeTypeDesignation(designation);
1093
                }
1094
            }
1095
            // delete IndividualsAssociation
1096
            if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1097
                IndividualsAssociation assciation = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1098
                assciation.setAssociatedSpecimenOrObservation(null);
1099
                assciation.getInDescription().removeElement(assciation);
1100
            }
1101
            // check for taxon description
1102
            if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1103
                TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1104
                taxonDescription.setDescribedSpecimenOrObservation(null);
1105
            }
1106
            // check for specimen description
1107
            if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1108
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1109
                // check if specimen is "described" specimen
1110
                if (specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)) {
1111
                    specimenDescription.setDescribedSpecimenOrObservation(null);
1112
                }
1113
                // check if description is a description of the given specimen
1114
                if (specimen.getDescriptions().contains(specimenDescription)) {
1115
                    specimen.removeDescription(specimenDescription);
1116
                }
1117
            }
1118
            // check for amplification
1119
            if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1120
                AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1121
                amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1122
            }
1123
            // check for sequence
1124
            if (relatedObject.isInstanceOf(Sequence.class)) {
1125
                Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1126
                sequence.getDnaSample().removeSequence(sequence);
1127
            }
1128
            // check for children and parents (derivation events)
1129
            if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1130
                DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1131
                // parent derivation event (derivedFrom)
1132
                if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1133
                    derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1134
                    if (derivationEvent.getDerivatives().isEmpty()) {
1135
                        Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1136
                        for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1137
                            specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1138
                        }
1139
                    }
1140
                }
1141
                else{
1142
                    //child derivation events should not occur since we delete the hierarchy from bottom to top
1143
                }
1144
            }
1145
        }
1146

    
1147
        deleteResult.includeResult(delete(specimen));
1148
        return deleteResult;
1149
    }
1150

    
1151
    @Override
1152
    public DeleteResult deleteSingleRead(SingleRead singleRead, Sequence sequence){
1153
        DeleteResult deleteResult = new DeleteResult();
1154
        singleRead = HibernateProxyHelper.deproxy(singleRead, SingleRead.class);
1155
        //delete from amplification result
1156
        if(singleRead.getAmplificationResult()!=null){
1157
            singleRead.getAmplificationResult().removeSingleRead(singleRead);
1158
        }
1159
        //delete from sequence
1160
        sequence.removeSingleRead(singleRead);
1161
        deleteResult.setStatus(Status.OK);
1162
        return deleteResult;
1163
    }
1164

    
1165
    @Override
1166
    public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {
1167
        DeleteResult deleteResult = new DeleteResult();
1168
        String deleteMolecularNotAllowed = "Deleting molecular data is not allowed in config";
1169
        if (from.isInstanceOf(Sequence.class)) {
1170
            if (!config.isDeleteMolecularData()) {
1171
                deleteResult.setAbort();
1172
                deleteResult.addException(new ReferencedObjectUndeletableException(deleteMolecularNotAllowed));
1173
                return deleteResult;
1174
            }
1175
            Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);
1176
            sequence.getDnaSample().removeSequence(sequence);
1177
            deleteResult = sequenceService.delete(sequence);
1178
        }
1179
        else if(from instanceof SingleRead){
1180
            SingleRead singleRead = (SingleRead)from;
1181
            //delete from amplification result
1182
            if(singleRead.getAmplificationResult()!=null){
1183
                singleRead.getAmplificationResult().removeSingleRead(singleRead);
1184
            }
1185
            deleteResult.setAbort();
1186
            deleteResult.addException(new ReferencedObjectUndeletableException("Deleted ONLY from amplification. "
1187
                    + "Single read may still be attached to a consensus sequence."));
1188
        }
1189
        else if(from.isInstanceOf(SpecimenOrObservationBase.class))  {
1190
            deleteResult = delete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config);
1191
        }
1192
        return deleteResult;
1193
    }
1194

    
1195
//    private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){
1196
    // Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();
1197
    // for (DerivationEvent derivationEvent : derivationEvents) {
1198
    // Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1199
    // for (DerivedUnit derivedUnit : derivatives) {
1200
    // DeleteResult deleteResult = deepDelete(derivedUnit, config);
1201
    // if(!deleteResult.isOk()){
1202
    // return deleteResult;
1203
    // }
1204
    // }
1205
    // }
1206
    // return delete(entity, config);
1207
    // }
1208

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

    
1214
    @Override
1215
    public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,
1216
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1217
        return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1218
    }
1219

    
1220
    @Override
1221
    public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1222
            SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1223
            List<String> propertyPaths) {
1224
        return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1225
    }
1226

    
1227
    @Override
1228
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1229
        Collection<DescriptionElementBase> states = new ArrayList<DescriptionElementBase>();
1230
        if (specimen != null) {
1231
            Set<DescriptionBase> descriptions = specimen.getDescriptions();
1232
            for (DescriptionBase<?> descriptionBase : descriptions) {
1233
                if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
1234
                    SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
1235
                    Set<DescriptionElementBase> elements = specimenDescription.getElements();
1236
                    for (DescriptionElementBase descriptionElementBase : elements) {
1237
                        if(descriptionElementBase.getFeature().isSupportsCategoricalData()
1238
                                ||descriptionElementBase.getFeature().isSupportsQuantitativeData()){
1239
                            states.add(descriptionElementBase);
1240
                        }
1241
                    }
1242
                }
1243
            }
1244
        }
1245
        return states;
1246
    }
1247

    
1248
    @Override
1249
    public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1250
        SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1251
        if (specimen != null) {
1252
            return getCharacterDataForSpecimen(specimen);
1253
        }
1254
        else{
1255
            throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1256
        }
1257
    }
1258

    
1259

    
1260
    @Override
1261
    public Pager<SpecimenOrObservationBase> findByTitle(
1262
            IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1263
        if (config instanceof FindOccurrencesConfigurator) {
1264
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1265
            List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1266
            Taxon taxon = null;
1267
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1268
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1269
                if(taxonBase.isInstanceOf(Taxon.class)){
1270
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1271
                }
1272
            }
1273
            occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1274
                    occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1275
                    occurrenceConfig.getSpecimenType(), taxon, occurrenceConfig.getMatchMode(), null, null,
1276
                    occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1277
            // indirectly associated specimens
1278
            List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1279
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1280
                for (SpecimenOrObservationBase specimen : occurrences) {
1281
                    List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1282
                    for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1283
                        if(!occurrences.contains(specimenOrObservationBase)){
1284
                            indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1285
                        }
1286
                    }
1287
                }
1288
                occurrences = indirectlyAssociatedOccurrences;
1289
            }
1290

    
1291
            return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1292
        }
1293
        return super.findByTitle(config);
1294
    }
1295

    
1296
    @Override
1297
    public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1298
        List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1299
        Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1300
        if(fieldUnits.isEmpty()){
1301
            allHierarchyDerivatives.add(specimen);
1302
            allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1303
        }
1304
        else{
1305
            for (FieldUnit fieldUnit : fieldUnits) {
1306
                allHierarchyDerivatives.add(fieldUnit);
1307
                allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1308
            }
1309
        }
1310
        return allHierarchyDerivatives;
1311
    }
1312

    
1313
    @Override
1314
    public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1315
        List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1316
        Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1317
        for (DerivationEvent derivationEvent : derivationEvents) {
1318
            Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1319
            for (DerivedUnit derivedUnit : derivatives) {
1320
                childDerivate.add(derivedUnit);
1321
                childDerivate.addAll(getAllChildDerivatives(derivedUnit));
1322
            }
1323
        }
1324
        return childDerivate;
1325
    }
1326

    
1327
    @Override
1328
    public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1329
        if (config instanceof FindOccurrencesConfigurator) {
1330
            FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1331
            Taxon taxon = null;
1332
            if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1333
                TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1334
                if(taxonBase.isInstanceOf(Taxon.class)){
1335
                    taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1336
                }
1337
            }
1338
            // indirectly associated specimens
1339
            if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1340
                return findByTitle(config).getRecords().size();
1341
            }
1342
            return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
1343
                    occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon,
1344
                    occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
1345
                    occurrenceConfig.getPropertyPaths());
1346
        }
1347
        return super.countByTitle(config);
1348
    }
1349

    
1350
}
(75-75/92)