c2d5f713ee2cc6db0b57b83356b92803c19b0f38
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / OccurrenceServiceImpl.java
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.molecular.ISingleReadDao;
110 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
111 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
112 import eu.etaxonomy.cdm.persistence.query.OrderHint;
113 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
114
115 /**
116 * @author a.babadshanjan
117 * @created 01.09.2008
118 */
119 @Service
120 @Transactional(readOnly = true)
121 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase, IOccurrenceDao> implements IOccurrenceService {
122
123 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
124
125 @Autowired
126 private IDefinedTermDao definedTermDao;
127
128 @Autowired
129 private IDescriptionService descriptionService;
130
131 @Autowired
132 private ITaxonService taxonService;
133
134 @Autowired
135 private ISequenceService sequenceService;
136
137 @Autowired
138 private ISingleReadDao singleReadDao;
139
140 @Autowired
141 private AbstractBeanInitializer beanInitializer;
142
143 @Autowired
144 private ILuceneIndexToolProvider luceneIndexToolProvider;
145
146 private static final String SEPARATOR_STRING = ", ";
147
148 public OccurrenceServiceImpl() {
149 logger.debug("Load OccurrenceService Bean");
150 }
151
152
153 @Override
154 @Transactional(readOnly = false)
155 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
156 if (clazz == null) {
157 clazz = SpecimenOrObservationBase.class;
158 }
159 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
160 }
161
162 /**
163 * FIXME Candidate for harmonization
164 * move to termService
165 */
166 @Override
167 public Country getCountryByIso(String iso639) {
168 return this.definedTermDao.getCountryByIso(iso639);
169
170 }
171
172 /**
173 * FIXME Candidate for harmonization
174 * move to termService
175 */
176 @Override
177 public List<Country> getCountryByName(String name) {
178 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
179 List<Country> countries = new ArrayList<Country>();
180 for (int i = 0; i < terms.size(); i++) {
181 countries.add((Country) terms.get(i));
182 }
183 return countries;
184 }
185
186 @Override
187 @Autowired
188 protected void setDao(IOccurrenceDao dao) {
189 this.dao = dao;
190 }
191
192 @Override
193 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
194 Integer numberOfResults = dao.countDerivationEvents(occurence);
195
196 List<DerivationEvent> results = new ArrayList<DerivationEvent>();
197 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
198 results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
199 }
200
201 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
202 }
203
204 @Override
205 public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
206 return dao.countDeterminations(occurence, taxonbase);
207 }
208
209 @Override
210 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
211 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
212
213 List<DeterminationEvent> results = new ArrayList<DeterminationEvent>();
214 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
215 results = dao.getDeterminations(occurrence, taxonBase, pageSize, pageNumber, propertyPaths);
216 }
217
218 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
219 }
220
221 @Override
222 public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
223 Integer numberOfResults = dao.countMedia(occurence);
224
225 List<Media> results = new ArrayList<Media>();
226 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
227 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
228 }
229
230 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
231 }
232
233 @Override
234 public Pager<Media> getMediainHierarchy(SpecimenOrObservationBase rootOccurence, Integer pageSize,
235 Integer pageNumber, List<String> propertyPaths) {
236 List<Media> media = new ArrayList<Media>();
237 //media specimens
238 if(rootOccurence.isInstanceOf(MediaSpecimen.class)){
239 MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(rootOccurence, MediaSpecimen.class);
240 media.add(mediaSpecimen.getMediaSpecimen());
241 }
242 // pherograms & gelPhotos
243 if (rootOccurence.isInstanceOf(DnaSample.class)) {
244 DnaSample dnaSample = CdmBase.deproxy(rootOccurence, DnaSample.class);
245 Set<Sequence> sequences = dnaSample.getSequences();
246 //we do show only those gelPhotos which lead to a consensus sequence
247 for (Sequence sequence : sequences) {
248 Set<Media> dnaRelatedMedia = new HashSet<Media>();
249 for (SingleRead singleRead : sequence.getSingleReads()){
250 AmplificationResult amplification = singleRead.getAmplificationResult();
251 dnaRelatedMedia.add(amplification.getGelPhoto());
252 dnaRelatedMedia.add(singleRead.getPherogram());
253 dnaRelatedMedia.remove(null);
254 }
255 media.addAll(dnaRelatedMedia);
256 }
257 }
258 if(rootOccurence.isInstanceOf(DerivedUnit.class)){
259 DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(rootOccurence, DerivedUnit.class);
260 for (DerivationEvent derivationEvent : derivedUnit.getDerivationEvents()) {
261 for (DerivedUnit childDerivative : derivationEvent.getDerivatives()) {
262 media.addAll(getMediainHierarchy(childDerivative, pageSize, pageNumber, propertyPaths).getRecords());
263 }
264 }
265 }
266 return new DefaultPagerImpl<Media>(pageNumber, media.size(), pageSize, media);
267 }
268
269 @Override
270 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
271 Integer numberOfResults = dao.count(type, determinedAs);
272 List<SpecimenOrObservationBase> results = new ArrayList<SpecimenOrObservationBase>();
273 pageNumber = pageNumber == null ? 0 : pageNumber;
274 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
275 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
276 results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
277 }
278 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
279 }
280
281 @Override
282 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
283 return dao.getDerivedUnitUuidAndTitleCache();
284 }
285
286 @Override
287 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
288 return dao.getFieldUnitUuidAndTitleCache();
289 }
290
291 @Override
292 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
293 derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
294 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
295 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
296 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
297 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
298 return derivedUnitFacade;
299 }
300
301 @Override
302 public List<DerivedUnitFacade> listDerivedUnitFacades(
303 DescriptionBase description, List<String> propertyPaths) {
304
305 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<DerivedUnitFacade>();
306 IndividualsAssociation tempIndividualsAssociation;
307 SpecimenOrObservationBase tempSpecimenOrObservationBase;
308 List<DescriptionElementBase> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
309 for (DescriptionElementBase element : elements) {
310 if (element.isInstanceOf(IndividualsAssociation.class)) {
311 tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
312 if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
313 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
314 if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
315 try {
316 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
317 } catch (DerivedUnitFacadeNotSupportedException e) {
318 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
319 }
320 }
321 }
322
323 }
324 }
325
326 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
327
328 return derivedUnitFacadeList;
329 }
330
331
332 @Override
333 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
334 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
335
336 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
337 }
338
339 @Override
340 public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
341 return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
342 }
343
344 @Override
345 public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
346 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
347 List<String> propertyPaths) {
348
349 if (!getSession().contains(associatedTaxon)) {
350 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
351 }
352
353 // gather the IDs of all relevant field units
354 Set<Integer> fieldUnitIds = new HashSet<Integer>();
355 List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
356 for (SpecimenOrObservationBase<?> specimen : records) {
357 for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
358 fieldUnitIds.add(fieldUnit.getId());
359 }
360 }
361 //dao.listByIds() does the paging of the field units. Passing the field units directly to the Pager would not work
362 List<SpecimenOrObservationBase> fieldUnits = dao.listByIds(fieldUnitIds, pageSize, pageNumber, orderHints, propertyPaths);
363 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitIds.size(), pageSize, fieldUnits);
364 }
365
366 @Override
367 public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
368
369 if (!getSession().contains(fieldUnit)) {
370 fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
371 }
372 TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
373
374 FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
375
376 if (fieldUnit.getGatheringEvent() != null) {
377 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
378 // Country
379 NamedArea country = gatheringEvent.getCountry();
380 fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
381 // Collection
382 AgentBase collector = gatheringEvent.getCollector();
383 String fieldNumber = fieldUnit.getFieldNumber();
384 String collectionString = "";
385 if (collector != null || fieldNumber != null) {
386 collectionString += collector != null ? collector : "";
387 if (!collectionString.isEmpty()) {
388 collectionString += " ";
389 }
390 collectionString += (fieldNumber != null ? fieldNumber : "");
391 collectionString.trim();
392 }
393 fieldUnitDTO.setCollection(collectionString);
394 // Date
395 Partial gatheringDate = gatheringEvent.getGatheringDate();
396 String dateString = null;
397 if (gatheringDate != null) {
398 gatheringDate.toString();
399 }
400 else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
401 dateString = gatheringEvent.getTimeperiod().getFreeText();
402 }
403 fieldUnitDTO.setDate(dateString);
404 }
405
406 // Taxon Name
407 fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
408
409 // Herbaria map
410 Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<eu.etaxonomy.cdm.model.occurrence.Collection, Integer>();
411 // List of accession numbers for citation
412 List<String> preservedSpecimenAccessionNumbers = new ArrayList<String>();
413
414 // assemble preserved specimen DTOs
415 Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
416 for (DerivationEvent derivationEvent : derivationEvents) {
417 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
418 for (DerivedUnit derivedUnit : derivatives) {
419 if(!derivedUnit.isPublish()){
420 continue;
421 }
422 // collect accession numbers for citation
423 String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
424 if (mostSignificantIdentifier != null) {
425 preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
426 }
427 // collect collections for herbaria column
428 if (derivedUnit.getCollection() != null) {
429 Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
430 if (herbariumCount == null) {
431 herbariumCount = 0;
432 }
433 collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
434 }
435 if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
436 PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
437 fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
438 fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
439 fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
440 fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
441 fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
442 }
443 }
444 }
445 // assemble derivate data DTO
446 assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
447
448 // assemble citation
449 String citation = fieldUnit.getTitleCache();
450 if (!preservedSpecimenAccessionNumbers.isEmpty()) {
451 citation += " (";
452 for (String accessionNumber : preservedSpecimenAccessionNumbers) {
453 if (!accessionNumber.isEmpty()) {
454 citation += accessionNumber + SEPARATOR_STRING;
455 }
456 }
457 citation = removeTail(citation, SEPARATOR_STRING);
458 citation += ")";
459 }
460 fieldUnitDTO.setCitation(citation);
461
462 // assemble herbaria string
463 String herbariaString = "";
464 for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
465 eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
466 if (collection.getCode() != null) {
467 herbariaString += collection.getCode();
468 }
469 if (e.getValue() > 1) {
470 herbariaString += "(" + e.getValue() + ")";
471 }
472 herbariaString += SEPARATOR_STRING;
473 }
474 herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
475 fieldUnitDTO.setHerbarium(herbariaString);
476
477 return fieldUnitDTO;
478 }
479
480 @Override
481 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
482 return assemblePreservedSpecimenDTO(derivedUnit, null);
483 }
484
485 @Override
486 public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
487 if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
488 return derivedUnit.getAccessionNumber();
489 }
490 else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
491 return derivedUnit.getBarcode();
492 }
493 else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
494 return derivedUnit.getCatalogNumber();
495 }
496 return null;
497 }
498
499 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
500 if (!getSession().contains(derivedUnit)) {
501 derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
502 }
503 PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
504
505 // check identifiers in priority order accNo>barCode>catalogNumber
506 if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
507 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getAccessionNumber());
508 }
509 else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
510 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getBarcode());
511 }
512 else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
513 preservedSpecimenDTO.setAccessionNumber(derivedUnit.getCatalogNumber());
514 }
515 preservedSpecimenDTO.setUuid(derivedUnit.getUuid().toString());
516
517 // citation
518 Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
519 if (fieldUnits.size() == 1) {
520 preservedSpecimenDTO.setCitation(fieldUnits.iterator().next().getTitleCache());
521 }
522 else{
523 preservedSpecimenDTO.setCitation("No Citation available. This specimen either has no or multiple field units.");
524 }
525
526 // character state data
527 Collection<DescriptionElementBase> characterDataForSpecimen = getCharacterDataForSpecimen(derivedUnit);
528 if (!characterDataForSpecimen.isEmpty()) {
529 if (fieldUnitDTO != null) {
530 fieldUnitDTO.setHasCharacterData(true);
531 }
532 }
533 for (DescriptionElementBase descriptionElementBase : characterDataForSpecimen) {
534 String character = descriptionElementBase.getFeature().getLabel();
535 ArrayList<Language> languages = new ArrayList<Language>(Collections.singleton(Language.DEFAULT()));
536 if (descriptionElementBase instanceof QuantitativeData) {
537 QuantitativeData quantitativeData = (QuantitativeData) descriptionElementBase;
538 DefaultQuantitativeDescriptionBuilder builder = new DefaultQuantitativeDescriptionBuilder();
539 String state = builder.build(quantitativeData, languages).getText(Language.DEFAULT());
540 preservedSpecimenDTO.addCharacterData(character, state);
541 }
542 else if(descriptionElementBase instanceof CategoricalData){
543 CategoricalData categoricalData = (CategoricalData) descriptionElementBase;
544 DefaultCategoricalDescriptionBuilder builder = new DefaultCategoricalDescriptionBuilder();
545 String state = builder.build(categoricalData, languages).getText(Language.DEFAULT());
546 preservedSpecimenDTO.addCharacterData(character, state);
547 }
548 }
549 // check type designations
550 Collection<SpecimenTypeDesignation> specimenTypeDesignations = listTypeDesignations(derivedUnit, null, null, null, null);
551 for (SpecimenTypeDesignation specimenTypeDesignation : specimenTypeDesignations) {
552 if (fieldUnitDTO != null) {
553 fieldUnitDTO.setHasType(true);
554 }
555 TypeDesignationStatusBase<?> typeStatus = specimenTypeDesignation.getTypeStatus();
556 if (typeStatus != null) {
557 List<String> typedTaxaNames = new ArrayList<String>();
558 String label = typeStatus.getLabel();
559 Set<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
560 for (TaxonNameBase taxonNameBase : typifiedNames) {
561 typedTaxaNames.add(taxonNameBase.getFullTitleCache());
562 }
563 preservedSpecimenDTO.addTypes(label, typedTaxaNames);
564 }
565 }
566
567 // individuals associations
568 Collection<IndividualsAssociation> individualsAssociations = listIndividualsAssociations(derivedUnit, null, null, null, null);
569 for (IndividualsAssociation individualsAssociation : individualsAssociations) {
570 if (individualsAssociation.getInDescription() != null) {
571 if (individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)) {
572 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
573 Taxon taxon = taxonDescription.getTaxon();
574 if (taxon != null && taxon.getName() != null) {
575 preservedSpecimenDTO.addAssociatedTaxon(taxon.getName().getTitleCache());
576 }
577 }
578 }
579 }
580 // assemble sub derivates
581 preservedSpecimenDTO.setDerivateDataDTO(assembleDerivateDataDTO(preservedSpecimenDTO, derivedUnit));
582 return preservedSpecimenDTO;
583 }
584
585 private DerivateDataDTO assembleDerivateDataDTO(DerivateDTO derivateDTO, SpecimenOrObservationBase<?> specimenOrObservation) {
586 DerivateDataDTO derivateDataDTO = new DerivateDataDTO();
587 Collection<DerivedUnit> childDerivates = getDerivedUnitsFor(specimenOrObservation);
588 for (DerivedUnit childDerivate : childDerivates) {
589 // assemble molecular data
590 //pattern: DNAMarker [contig1, primer1_1, primer1_2, ...][contig2, primer2_1, ...]...
591 if (childDerivate.isInstanceOf(DnaSample.class)) {
592 if (childDerivate.getRecordBasis() == SpecimenOrObservationType.TissueSample) {
593 // TODO implement TissueSample assembly for web service
594 }
595 if (childDerivate.getRecordBasis() == SpecimenOrObservationType.DnaSample) {
596
597 DnaSample dna = HibernateProxyHelper.deproxy(childDerivate, DnaSample.class);
598 if (!dna.getSequences().isEmpty()) {
599 derivateDTO.setHasDna(true);
600 }
601 for (Sequence sequence : dna.getSequences()) {
602 URI boldUri = null;
603 try {
604 boldUri = sequence.getBoldUri();
605 } catch (URISyntaxException e1) {
606 logger.error("Could not create BOLD URI", e1);
607 }
608 final DefinedTerm dnaMarker = sequence.getDnaMarker();
609 MolecularData molecularData = derivateDataDTO.addProviderLink(boldUri != null ? boldUri : null, dnaMarker != null ? dnaMarker.getLabel() : "[no marker]");
610
611 //contig file
612 ContigFile contigFile = null;
613 if (sequence.getContigFile() != null) {
614 MediaRepresentationPart contigMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(sequence.getContigFile());
615 if (contigMediaRepresentationPart != null) {
616 contigFile = molecularData.addContigFile(contigMediaRepresentationPart.getUri(), "contig");
617 }
618 }
619 if(contigFile==null){
620 contigFile = molecularData.addContigFile(null, "[no contig]");
621 }
622 // primer files
623 if (sequence.getSingleReads() != null) {
624 int readCount = 1;
625 for (SingleRead singleRead : sequence.getSingleReads()) {
626 MediaRepresentationPart pherogramMediaRepresentationPart = MediaUtils.getFirstMediaRepresentationPart(singleRead.getPherogram());
627 if (pherogramMediaRepresentationPart != null) {
628 contigFile.addPrimerLink(pherogramMediaRepresentationPart.getUri(), "read"+readCount++);
629 }
630 }
631 }
632 }
633 }
634 }
635 // assemble media data
636 else if (childDerivate.isInstanceOf(MediaSpecimen.class)) {
637 MediaSpecimen media = HibernateProxyHelper.deproxy(childDerivate, MediaSpecimen.class);
638
639 String mediaUriString = getMediaUriString(media);
640 if (media.getKindOfUnit() != null) {
641 // specimen scan
642 if (media.getKindOfUnit().getUuid().equals(UUID.fromString("acda15be-c0e2-4ea8-8783-b9b0c4ad7f03"))) {
643 derivateDataDTO.addSpecimenScanUuid(media.getMediaSpecimen().getUuid());
644 derivateDTO.setHasSpecimenScan(true);
645 String imageLinkText = "scan";
646 if (derivateDTO instanceof PreservedSpecimenDTO && ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber() != null) {
647 imageLinkText = ((PreservedSpecimenDTO) derivateDTO).getAccessionNumber();
648 }
649 derivateDataDTO.addSpecimenScan(mediaUriString == null ? "" : mediaUriString, imageLinkText);
650 }
651 // detail image
652 else if (media.getKindOfUnit().getUuid().equals(UUID.fromString("31eb8d02-bf5d-437c-bcc6-87a626445f34"))) {
653 derivateDataDTO.addDetailImageUuid(media.getMediaSpecimen().getUuid());
654 derivateDTO.setHasDetailImage(true);
655 String motif = "";
656 if (media.getMediaSpecimen() != null && media.getMediaSpecimen().getTitle() != null) {
657 motif = media.getMediaSpecimen().getTitle().getText();
658 }
659 derivateDataDTO.addDetailImage(mediaUriString == null ? "" : mediaUriString, motif != null ? motif : "[no motif]");
660 }
661 }
662 }
663 }
664 return derivateDataDTO;
665 }
666
667 private String removeTail(String string, final String tail) {
668 if (string.endsWith(tail)) {
669 string = string.substring(0, string.length() - tail.length());
670 }
671 return string;
672 }
673
674 private String getMediaUriString(MediaSpecimen mediaSpecimen) {
675 String mediaUri = null;
676 Collection<MediaRepresentation> mediaRepresentations = mediaSpecimen.getMediaSpecimen().getRepresentations();
677 if (mediaRepresentations != null && !mediaRepresentations.isEmpty()) {
678 Collection<MediaRepresentationPart> mediaRepresentationParts = mediaRepresentations.iterator().next().getParts();
679 if (mediaRepresentationParts != null && !mediaRepresentationParts.isEmpty()) {
680 MediaRepresentationPart part = mediaRepresentationParts.iterator().next();
681 if (part.getUri() != null) {
682 mediaUri = part.getUri().toASCIIString();
683 }
684 }
685 }
686 return mediaUri;
687 }
688
689 private Collection<DerivedUnit> getDerivedUnitsFor(SpecimenOrObservationBase<?> specimen) {
690 Collection<DerivedUnit> derivedUnits = new ArrayList<DerivedUnit>();
691 for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
692 for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
693 derivedUnits.add(derivative);
694 derivedUnits.addAll(getDerivedUnitsFor(derivative));
695 }
696 }
697 return derivedUnits;
698 }
699
700
701 @SuppressWarnings("unchecked")
702 @Override
703 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
704 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
705
706 Set<Taxon> taxa = new HashSet<Taxon>();
707 Set<Integer> occurrenceIds = new HashSet<Integer>();
708 List<T> occurrences = new ArrayList<T>();
709
710 // Integer limit = PagerUtils.limitFor(pageSize);
711 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
712
713 if (!getSession().contains(associatedTaxon)) {
714 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
715 }
716
717 if (includeRelationships != null) {
718 taxa = taxonService.listRelatedTaxa(associatedTaxon, includeRelationships, maxDepth, null, null, propertyPaths);
719 }
720
721 taxa.add(associatedTaxon);
722
723 for (Taxon taxon : taxa) {
724 List<T> perTaxonOccurrences = dao.listByAssociatedTaxon(type, taxon, null, null, orderHints, propertyPaths);
725 for (SpecimenOrObservationBase o : perTaxonOccurrences) {
726 occurrenceIds.add(o.getId());
727 }
728 }
729 occurrences = (List<T>) dao.listByIds(occurrenceIds, pageSize, pageNumber, orderHints, propertyPaths);
730
731 return new DefaultPagerImpl<T>(pageNumber, occurrenceIds.size(), pageSize, occurrences);
732
733 }
734
735 @Override
736 public <T extends SpecimenOrObservationBase> Pager<T> pageByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
737 String taxonUUID, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
738
739 UUID uuid = UUID.fromString(taxonUUID);
740 Taxon tax = (Taxon) taxonService.load(uuid);
741 // TODO REMOVE NULL STATEMENT
742 type = null;
743 return pageByAssociatedTaxon(type, includeRelationships, tax, maxDepth, pageSize, pageNumber, orderHints, propertyPaths);
744
745 }
746
747 @Override
748 public Pager<SearchResult<SpecimenOrObservationBase>> findByFullText(
749 Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle boundingBox, List<Language> languages,
750 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
751 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
752
753 LuceneSearch luceneSearch = prepareByFullTextSearch(clazz, queryString, boundingBox, languages, highlightFragments);
754
755 // --- execute search
756 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
757
758 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
759 idFieldMap.put(CdmBaseType.SPECIMEN_OR_OBSERVATIONBASE, "id");
760
761 // --- initialize taxa, highlight matches ....
762 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
763 @SuppressWarnings("rawtypes")
764 List<SearchResult<SpecimenOrObservationBase>> searchResults = searchResultBuilder.createResultSet(
765 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
766
767 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
768
769 return new DefaultPagerImpl<SearchResult<SpecimenOrObservationBase>>(pageNumber, totalHits, pageSize,
770 searchResults);
771
772 }
773
774 private LuceneSearch prepareByFullTextSearch(Class<? extends SpecimenOrObservationBase> clazz, String queryString, Rectangle bbox,
775 List<Language> languages, boolean highlightFragments) {
776
777 BooleanQuery finalQuery = new BooleanQuery();
778 BooleanQuery textQuery = new BooleanQuery();
779
780 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, FieldUnit.class);
781 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(FieldUnit.class);
782
783 // --- criteria
784 luceneSearch.setCdmTypRestriction(clazz);
785 if (queryString != null) {
786 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
787 finalQuery.add(textQuery, Occur.MUST);
788 }
789
790 // --- spacial query
791 if (bbox != null) {
792 finalQuery.add(QueryFactory.buildSpatialQueryByRange(bbox, "gatheringEvent.exactLocation.point"), Occur.MUST);
793 }
794
795 luceneSearch.setQuery(finalQuery);
796
797 // --- sorting
798 SortField[] sortFields = new SortField[] { SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false) };
799 luceneSearch.setSortFields(sortFields);
800
801 if (highlightFragments) {
802 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
803 }
804 return luceneSearch;
805 }
806
807
808 @Override
809 public Collection<FieldUnit> getFieldUnits(UUID derivedUnitUuid) {
810 //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
811 //from which this DerivedUnit was derived until all FieldUnits are found.
812
813 // FIXME: use HQL queries to increase performance
814 SpecimenOrObservationBase<?> specimen = load(derivedUnitUuid);
815 // specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
816 Collection<FieldUnit> fieldUnits = new ArrayList<FieldUnit>();
817
818 if (specimen.isInstanceOf(FieldUnit.class)) {
819 fieldUnits.add(HibernateProxyHelper.deproxy(specimen, FieldUnit.class));
820 }
821 else if(specimen.isInstanceOf(DerivedUnit.class)){
822 fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class)));
823 }
824 return fieldUnits;
825 }
826
827 private Collection<FieldUnit> getFieldUnits(DerivedUnit derivedUnit) {
828 Collection<FieldUnit> fieldUnits = new HashSet<FieldUnit>();
829 Set<SpecimenOrObservationBase> originals = derivedUnit.getOriginals();
830 if (originals != null && !originals.isEmpty()) {
831 for (SpecimenOrObservationBase<?> original : originals) {
832 if (original.isInstanceOf(FieldUnit.class)) {
833 fieldUnits.add(HibernateProxyHelper.deproxy(original, FieldUnit.class));
834 }
835 else if(original.isInstanceOf(DerivedUnit.class)){
836 fieldUnits.addAll(getFieldUnits(HibernateProxyHelper.deproxy(original, DerivedUnit.class)));
837 }
838 }
839 }
840 return fieldUnits;
841 }
842
843 @Override
844 @Transactional(readOnly = false)
845 public UpdateResult moveSequence(DnaSample from, DnaSample to, Sequence sequence) {
846 return moveSequence(from.getUuid(), to.getUuid(), sequence.getUuid());
847 }
848
849 @Override
850 @Transactional(readOnly = false)
851 public UpdateResult moveSequence(UUID fromUuid, UUID toUuid, UUID sequenceUuid) {
852 // reload specimens to avoid session conflicts
853 DnaSample from = (DnaSample) load(fromUuid);
854 DnaSample to = (DnaSample) load(toUuid);
855 Sequence sequence = sequenceService.load(sequenceUuid);
856
857 if (from == null || to == null || sequence == null) {
858 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" +
859 "Operation was move "+sequence+ " from "+from+" to "+to);
860 }
861 UpdateResult result = new UpdateResult();
862 from.removeSequence(sequence);
863 saveOrUpdate(from);
864 to.addSequence(sequence);
865 saveOrUpdate(to);
866 result.setStatus(Status.OK);
867 result.addUpdatedObject(from);
868 result.addUpdatedObject(to);
869 return result;
870 }
871
872 @Override
873 @Transactional(readOnly = false)
874 public boolean moveDerivate(SpecimenOrObservationBase<?> from, SpecimenOrObservationBase<?> to, DerivedUnit derivate) {
875 return moveDerivate(from!=null?from.getUuid():null, to.getUuid(), derivate.getUuid()).isOk();
876 }
877
878 @Override
879 @Transactional(readOnly = false)
880 public UpdateResult moveDerivate(UUID specimenFromUuid, UUID specimenToUuid, UUID derivateUuid) {
881 // reload specimens to avoid session conflicts
882 SpecimenOrObservationBase<?> from = null;
883 if(specimenFromUuid!=null){
884 from = load(specimenFromUuid);
885 }
886 SpecimenOrObservationBase<?> to = load(specimenToUuid);
887 DerivedUnit derivate = (DerivedUnit) load(derivateUuid);
888
889 if ((specimenFromUuid!=null && from == null) || to == null || derivate == null) {
890 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" +
891 "Operation was move "+derivate+ " from "+from+" to "+to);
892 }
893 UpdateResult result = new UpdateResult();
894 SpecimenOrObservationType derivateType = derivate.getRecordBasis();
895 SpecimenOrObservationType toType = to.getRecordBasis();
896 // check if type is a sub derivate type
897 if(toType==SpecimenOrObservationType.FieldUnit //moving to FieldUnit always works
898 || derivateType==SpecimenOrObservationType.Media //moving media always works
899 || (derivateType.isKindOf(toType) && toType!=derivateType)){ //moving only to parent derivate type
900 if(from!=null){
901 // remove derivation event from parent specimen of dragged object
902 DerivationEvent eventToRemove = null;
903 for (DerivationEvent event : from.getDerivationEvents()) {
904 if (event.getDerivatives().contains(derivate)) {
905 eventToRemove = event;
906 break;
907 }
908 }
909 from.removeDerivationEvent(eventToRemove);
910 if(eventToRemove!=null){
911 // add new derivation event to target and copy the event parameters of the old one
912 DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
913 derivedFromNewOriginalEvent.setActor(eventToRemove.getActor());
914 derivedFromNewOriginalEvent.setDescription(eventToRemove.getDescription());
915 derivedFromNewOriginalEvent.setInstitution(eventToRemove.getInstitution());
916 derivedFromNewOriginalEvent.setTimeperiod(eventToRemove.getTimeperiod());
917 derivedFromNewOriginalEvent.setType(eventToRemove.getType());
918 to.addDerivationEvent(derivedFromNewOriginalEvent);
919 derivate.setDerivedFrom(derivedFromNewOriginalEvent);
920 }
921 }
922 else{
923 //derivative had no parent before so we use empty derivation event
924 DerivationEvent derivedFromNewOriginalEvent = DerivationEvent.NewSimpleInstance(to, derivate, null);
925 to.addDerivationEvent(derivedFromNewOriginalEvent);
926 derivate.setDerivedFrom(derivedFromNewOriginalEvent);
927 }
928
929 if(from!=null){
930 saveOrUpdate(from);
931 }
932 saveOrUpdate(to);
933 result.setStatus(Status.OK);
934 result.addUpdatedObject(from);
935 result.addUpdatedObject(to);
936 } else {
937 result.setStatus(Status.ERROR);
938 }
939 return result;
940 }
941
942 @Override
943 public Collection<ICdmBase> getNonCascadedAssociatedElements(SpecimenOrObservationBase<?> specimen) {
944 // potential fields that are not persisted cascadingly
945 /*
946 * SOOB
947 -DescriptionBase
948 -determinations
949 --modifier TERM
950 -kindOfUnit TERM
951 -lifeStage TERM
952 -sex TERM
953
954 FieldUnit
955 -GatheringEvent
956 --Country TERM
957 --CollectingAreas TERM
958
959 DerivedUnit
960 -collection
961 --institute
962 ---types TERM
963 -preservationMethod
964 --medium TERM
965 -storedUnder CDM TaxonNameBase
966 */
967
968 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
969
970 //Choose the correct entry point to traverse the graph (FieldUnit or DerivedUnit)
971
972 // FieldUnit
973 if (specimen.isInstanceOf(FieldUnit.class)) {
974 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(HibernateProxyHelper.deproxy(specimen, FieldUnit.class)));
975 }
976 // DerivedUnit
977 else if (specimen.isInstanceOf(DerivedUnit.class)) {
978 DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(specimen, DerivedUnit.class);
979 if (derivedUnit.getDerivedFrom() != null) {
980 Collection<FieldUnit> fieldUnits = getFieldUnits(derivedUnit);
981 for (FieldUnit fieldUnit : fieldUnits) {
982 nonCascadedCdmEntities.addAll(getFieldUnitNonCascadedAssociatedElements(fieldUnit));
983 }
984 }
985 }
986 return nonCascadedCdmEntities;
987 }
988
989 private Collection<ICdmBase> getFieldUnitNonCascadedAssociatedElements(FieldUnit fieldUnit) {
990 // get non cascaded element on SpecimenOrObservationBase level
991 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(fieldUnit);
992
993 // get FieldUnit specific elements
994 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
995 if (gatheringEvent != null) {
996 // country
997 if (gatheringEvent.getCountry() != null) {
998 nonCascadedCdmEntities.add(gatheringEvent.getCountry());
999 }
1000 // collecting areas
1001 for (NamedArea namedArea : gatheringEvent.getCollectingAreas()) {
1002 nonCascadedCdmEntities.add(namedArea);
1003 }
1004 }
1005 for (DerivationEvent derivationEvent : fieldUnit.getDerivationEvents()) {
1006 for (DerivedUnit derivedUnit : derivationEvent.getDerivatives()) {
1007 nonCascadedCdmEntities.addAll(getDerivedUnitNonCascadedAssociatedElements(derivedUnit));
1008 }
1009 }
1010 return nonCascadedCdmEntities;
1011 }
1012
1013 private Collection<ICdmBase> getDerivedUnitNonCascadedAssociatedElements(DerivedUnit derivedUnit) {
1014 // get non cascaded element on SpecimenOrObservationBase level
1015 Collection<ICdmBase> nonCascadedCdmEntities = getSpecimenOrObservationNonCascadedAssociatedElements(derivedUnit);
1016
1017 // get DerivedUnit specific elements
1018 if (derivedUnit.getCollection() != null && derivedUnit.getCollection().getInstitute() != null) {
1019 for (DefinedTerm type : derivedUnit.getCollection().getInstitute().getTypes()) {
1020 nonCascadedCdmEntities.add(type);
1021 }
1022 }
1023 if (derivedUnit.getPreservation() != null && derivedUnit.getPreservation().getMedium() != null) {
1024 nonCascadedCdmEntities.add(derivedUnit.getPreservation().getMedium());
1025 }
1026 if (derivedUnit.getStoredUnder() != null) {
1027 nonCascadedCdmEntities.add(derivedUnit.getStoredUnder());
1028 }
1029 return nonCascadedCdmEntities;
1030 }
1031
1032 private Collection<ICdmBase> getSpecimenOrObservationNonCascadedAssociatedElements(
1033 SpecimenOrObservationBase<?> specimen) {
1034 Collection<ICdmBase> nonCascadedCdmEntities = new HashSet<ICdmBase>();
1035 // scan SpecimenOrObservationBase
1036 for (DeterminationEvent determinationEvent : specimen.getDeterminations()) {
1037 // modifier
1038 if (determinationEvent.getModifier() != null) {
1039 nonCascadedCdmEntities.add(determinationEvent.getModifier());
1040 }
1041 }
1042 // kindOfUnit
1043 if (specimen.getKindOfUnit() != null) {
1044 nonCascadedCdmEntities.add(specimen.getKindOfUnit());
1045 }
1046 // lifeStage
1047 if (specimen.getLifeStage() != null) {
1048 nonCascadedCdmEntities.add(specimen.getLifeStage());
1049 }
1050 // sex
1051 if (specimen.getSex() != null) {
1052 nonCascadedCdmEntities.add(specimen.getSex());
1053 }
1054 return nonCascadedCdmEntities;
1055 }
1056
1057 @Override
1058 public DeleteResult isDeletable(SpecimenOrObservationBase specimen, DeleteConfiguratorBase config) {
1059 DeleteResult deleteResult = new DeleteResult();
1060 SpecimenDeleteConfigurator specimenDeleteConfigurator = (SpecimenDeleteConfigurator) config;
1061
1062 // check elements found by super method
1063 Set<CdmBase> relatedObjects = super.isDeletable(specimen, config).getRelatedObjects();
1064 for (CdmBase cdmBase : relatedObjects) {
1065 // check for type designation
1066 if (cdmBase.isInstanceOf(SpecimenTypeDesignation.class) && !specimenDeleteConfigurator.isDeleteFromTypeDesignation()) {
1067 deleteResult.setAbort();
1068 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is a type specimen."));
1069 deleteResult.addRelatedObject(cdmBase);
1070 break;
1071 }
1072 // check for IndividualsAssociations
1073 else if (cdmBase.isInstanceOf(IndividualsAssociation.class) && !specimenDeleteConfigurator.isDeleteFromIndividualsAssociation()) {
1074 deleteResult.setAbort();
1075 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still associated via IndividualsAssociations"));
1076 deleteResult.addRelatedObject(cdmBase);
1077 break;
1078 }
1079 // check for specimen/taxon description
1080 else if((cdmBase.isInstanceOf(SpecimenDescription.class) || cdmBase.isInstanceOf(TaxonDescription.class))
1081 && !specimenDeleteConfigurator.isDeleteFromDescription()){
1082 deleteResult.setAbort();
1083 deleteResult.addException(new ReferencedObjectUndeletableException("Specimen is still used in a Description."));
1084 deleteResult.addRelatedObject(cdmBase);
1085 break;
1086 }
1087 // check for children and parents (derivation events)
1088 else if (cdmBase.isInstanceOf(DerivationEvent.class)) {
1089 DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(cdmBase, DerivationEvent.class);
1090 // check if derivation event is empty
1091 if (!derivationEvent.getDerivatives().isEmpty() && derivationEvent.getOriginals().contains(specimen)) {
1092 // if derivationEvent is the childEvent and contains derivations
1093 // if (derivationEvent.getDerivatives().contains(specimen)) {
1094 // //if it is the parent event the specimen is still deletable
1095 // continue;
1096 // }
1097 if(!specimenDeleteConfigurator.isDeleteChildren()){
1098 //if children should not be deleted then it is undeletable
1099 deleteResult.setAbort();
1100 deleteResult.addException(new ReferencedObjectUndeletableException("Derivative still has child derivatives."));
1101 deleteResult.addRelatedObject(cdmBase);
1102 break;
1103 }
1104 else{
1105 // check all children if they can be deleted
1106 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1107 DeleteResult childResult = new DeleteResult();
1108 for (DerivedUnit derivedUnit : derivatives) {
1109 childResult.includeResult(isDeletable(derivedUnit, specimenDeleteConfigurator));
1110 }
1111 if (!childResult.isOk()) {
1112 deleteResult.setAbort();
1113 deleteResult.includeResult(childResult);
1114 deleteResult.addRelatedObject(cdmBase);
1115 break;
1116 }
1117 }
1118 }
1119 }
1120 // check for amplification
1121 else if (cdmBase.isInstanceOf(AmplificationResult.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1122 deleteResult.setAbort();
1123 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1124 deleteResult.addRelatedObject(cdmBase);
1125 break;
1126 }
1127 // check for sequence
1128 else if (cdmBase.isInstanceOf(Sequence.class) && !specimenDeleteConfigurator.isDeleteMolecularData()) {
1129 deleteResult.setAbort();
1130 deleteResult.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1131 deleteResult.addRelatedObject(cdmBase);
1132 break;
1133 }
1134 }
1135 if (deleteResult.isOk()) {
1136 //add all related object if deletion is OK so they can be handled by the delete() method
1137 deleteResult.addRelatedObjects(relatedObjects);
1138 }
1139 return deleteResult;
1140 }
1141
1142 @Override
1143 public DeleteResult delete(SpecimenOrObservationBase<?> specimen, SpecimenDeleteConfigurator config) {
1144 specimen = HibernateProxyHelper.deproxy(specimen, SpecimenOrObservationBase.class);
1145
1146 if (config.isDeleteChildren()) {
1147 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1148 //clone to avoid concurrent modification
1149 //can happen if the child is deleted and deleted its own derivedFrom event
1150 Set<DerivationEvent> derivationEventsClone = new HashSet<DerivationEvent>(derivationEvents);
1151 for (DerivationEvent derivationEvent : derivationEventsClone) {
1152 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1153 for (DerivedUnit derivedUnit : derivatives) {
1154 delete(derivedUnit, config);
1155 }
1156 }
1157 }
1158
1159 DeleteResult deleteResult = isDeletable(specimen, config);
1160 if (!deleteResult.isOk()) {
1161 return deleteResult;
1162 }
1163
1164 // check related objects
1165 Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1166
1167 for (CdmBase relatedObject : relatedObjects) {
1168 // check for TypeDesignations
1169 if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1170 SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1171 designation.setTypeSpecimen(null);
1172 List<TaxonNameBase> typifiedNames = new ArrayList<TaxonNameBase>();
1173 typifiedNames.addAll(designation.getTypifiedNames());
1174 for (TaxonNameBase taxonNameBase : typifiedNames) {
1175 taxonNameBase.removeTypeDesignation(designation);
1176 }
1177 }
1178 // delete IndividualsAssociation
1179 if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1180 IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1181 association.setAssociatedSpecimenOrObservation(null);
1182 association.getInDescription().removeElement(association);
1183 }
1184 // check for taxon description
1185 if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1186 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1187 taxonDescription.setDescribedSpecimenOrObservation(null);
1188 }
1189 // check for specimen description
1190 if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1191 SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1192 // check if specimen is "described" specimen
1193 if (specimenDescription.getDescribedSpecimenOrObservation().equals(specimen)) {
1194 specimenDescription.setDescribedSpecimenOrObservation(null);
1195 }
1196 // check if description is a description of the given specimen
1197 if (specimen.getDescriptions().contains(specimenDescription)) {
1198 specimen.removeDescription(specimenDescription);
1199 }
1200 DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription, null);
1201 if (descriptionDelete.isOk()){
1202 descriptionService.delete(specimenDescription);
1203 }
1204 }
1205 // check for amplification
1206 if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1207 AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1208 amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1209 }
1210 // check for sequence
1211 if (relatedObject.isInstanceOf(Sequence.class)) {
1212 Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1213 sequence.getDnaSample().removeSequence(sequence);
1214 }
1215 // check for children and parents (derivation events)
1216 if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1217 DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1218 // parent derivation event (derivedFrom)
1219 if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1220 derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1221 if (derivationEvent.getDerivatives().isEmpty()) {
1222 Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1223 for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1224 specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1225 deleteResult.addUpdatedObject(specimenOrObservationBase);
1226 }
1227 }
1228 }
1229 else{
1230 //child derivation events should not occur since we delete the hierarchy from bottom to top
1231 }
1232 }
1233 }
1234
1235 deleteResult.includeResult(delete(specimen));
1236 return deleteResult;
1237 }
1238
1239 @Override
1240 public DeleteResult deleteSingleRead(SingleRead singleRead, Sequence sequence){
1241 DeleteResult deleteResult = new DeleteResult();
1242 singleRead = HibernateProxyHelper.deproxy(singleRead, SingleRead.class);
1243 //delete from amplification result
1244 if(singleRead.getAmplificationResult()!=null){
1245 deleteResult.addUpdatedObject(singleRead.getAmplificationResult());
1246 singleRead.getAmplificationResult().removeSingleRead(singleRead);
1247 }
1248 //delete from sequence
1249 sequence.removeSingleRead(singleRead);
1250 deleteResult.addUpdatedObject(sequence);
1251
1252 //check if used in other sequences
1253 List<SingleRead> toDelete = new ArrayList<SingleRead>();
1254 Map<SingleRead, Collection<Sequence>> singleReadSequencesMap = sequenceService.getSingleReadSequencesMap();
1255 if(singleReadSequencesMap.containsKey(singleRead)){
1256 for (Entry<SingleRead, Collection<Sequence>> entry : singleReadSequencesMap.entrySet()) {
1257 if(entry.getValue().isEmpty()){
1258 toDelete.add(singleRead);
1259 }
1260 }
1261 for (SingleRead singleReadToDelete : toDelete) {
1262 singleReadDao.delete(singleReadToDelete);
1263 }
1264 }
1265 else{
1266 singleReadDao.delete(singleRead);
1267 }
1268 deleteResult.setStatus(Status.OK);
1269 return deleteResult;
1270 }
1271
1272 @Override
1273 @Transactional(readOnly = false)
1274 public DeleteResult deleteSingleRead(UUID singleReadUuid, UUID sequenceUuid){
1275 SingleRead singleRead = null;
1276 Sequence sequence = CdmBase.deproxy(sequenceService.load(sequenceUuid), Sequence.class);
1277 for(SingleRead sr : sequence.getSingleReads()) {
1278 if(sr.getUuid().equals(singleReadUuid)) {
1279 singleRead = sr;
1280 break;
1281 }
1282 }
1283 return deleteSingleRead(singleRead, sequence);
1284 }
1285
1286 @Override
1287 public DeleteResult deleteDerivateHierarchy(CdmBase from, SpecimenDeleteConfigurator config) {
1288 DeleteResult deleteResult = new DeleteResult();
1289 String deleteMolecularNotAllowed = "Deleting molecular data is not allowed in config";
1290 if (from.isInstanceOf(Sequence.class)) {
1291 if (!config.isDeleteMolecularData()) {
1292 deleteResult.setAbort();
1293 deleteResult.addException(new ReferencedObjectUndeletableException(deleteMolecularNotAllowed));
1294 return deleteResult;
1295 }
1296 Sequence sequence = HibernateProxyHelper.deproxy(from, Sequence.class);
1297 DnaSample dnaSample = sequence.getDnaSample();
1298 dnaSample.removeSequence(sequence);
1299 deleteResult.includeResult(sequenceService.delete(sequence));
1300 deleteResult.addUpdatedObject(dnaSample);
1301 }
1302 else if(from instanceof SingleRead){
1303 SingleRead singleRead = (SingleRead)from;
1304 //delete from amplification result
1305 if(singleRead.getAmplificationResult()!=null){
1306 singleRead.getAmplificationResult().removeSingleRead(singleRead);
1307 }
1308 deleteResult.setAbort();
1309 deleteResult.addException(new ReferencedObjectUndeletableException("Deleted ONLY from amplification. "
1310 + "Single read may still be attached to a consensus sequence."));
1311 }
1312 else if(from.isInstanceOf(SpecimenOrObservationBase.class)) {
1313 deleteResult.includeResult(delete(HibernateProxyHelper.deproxy(from, SpecimenOrObservationBase.class), config));
1314 }
1315 return deleteResult;
1316 }
1317
1318 @Override
1319 @Transactional(readOnly = false)
1320 public DeleteResult deleteDerivateHierarchy(UUID fromUuid, SpecimenDeleteConfigurator config) {
1321 return deleteDerivateHierarchy(dao.load(fromUuid),config);
1322 }
1323
1324 // private DeleteResult deepDelete(SpecimenOrObservationBase<?> entity, SpecimenDeleteConfigurator config){
1325 // Set<DerivationEvent> derivationEvents = entity.getDerivationEvents();
1326 // for (DerivationEvent derivationEvent : derivationEvents) {
1327 // Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1328 // for (DerivedUnit derivedUnit : derivatives) {
1329 // DeleteResult deleteResult = deepDelete(derivedUnit, config);
1330 // if(!deleteResult.isOk()){
1331 // return deleteResult;
1332 // }
1333 // }
1334 // }
1335 // return delete(entity, config);
1336 // }
1337
1338 @Override
1339 public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1340 return dao.listIndividualsAssociations(specimen, null, null, null, null);
1341 }
1342
1343 /**
1344 * {@inheritDoc}
1345 */
1346 @Override
1347 public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1348 List<OrderHint> orderHints, List<String> propertyPaths) {
1349 //individuals associations
1350 Collection<TaxonBase<?>> associatedTaxa = new HashSet<TaxonBase<?>>();
1351 for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1352 if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1353 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1354 associatedTaxa.add(taxonDescription.getTaxon());
1355 }
1356 }
1357 //type designation
1358 Collection<SpecimenTypeDesignation> typeDesignations = new HashSet<SpecimenTypeDesignation>();
1359 for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1360 if(typeDesignation.getTypeSpecimen().equals(specimen)){
1361 Set<TaxonNameBase> typifiedNames = typeDesignation.getTypifiedNames();
1362 for (TaxonNameBase taxonNameBase : typifiedNames) {
1363 associatedTaxa.addAll(taxonNameBase.getTaxa());
1364 }
1365 }
1366 }
1367 return associatedTaxa;
1368 }
1369
1370 @Override
1371 public Collection<SpecimenTypeDesignation> listTypeDesignations(SpecimenOrObservationBase<?> specimen,
1372 Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1373 return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1374 }
1375
1376 @Override
1377 public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1378 SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1379 List<String> propertyPaths) {
1380 return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1381 }
1382
1383 @Override
1384 @Deprecated //this is not a service layer task so it may be removed in future versions
1385 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1386 if (specimen != null) {
1387 return specimen.characterData();
1388 }else{
1389 return new ArrayList<DescriptionElementBase>();
1390 }
1391 }
1392
1393 @Override
1394 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1395 SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1396 if (specimen != null) {
1397 return getCharacterDataForSpecimen(specimen);
1398 }
1399 else{
1400 throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1401 }
1402 }
1403
1404
1405 @Override
1406 public Pager<SpecimenOrObservationBase> findByTitle(
1407 IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1408 if (config instanceof FindOccurrencesConfigurator) {
1409 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1410 List<SpecimenOrObservationBase> occurrences = new ArrayList<SpecimenOrObservationBase>();
1411 Taxon taxon = null;
1412 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1413 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1414 if(taxonBase.isInstanceOf(Taxon.class)){
1415 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1416 }
1417 }
1418 occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1419 occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1420 occurrenceConfig.getSpecimenType(), taxon, occurrenceConfig.getMatchMode(), null, null,
1421 occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1422 // indirectly associated specimens
1423 List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<SpecimenOrObservationBase>(occurrences);
1424 if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1425 for (SpecimenOrObservationBase specimen : occurrences) {
1426 List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1427 for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1428 if(!occurrences.contains(specimenOrObservationBase)){
1429 indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1430 }
1431 }
1432 }
1433 occurrences = indirectlyAssociatedOccurrences;
1434 }
1435
1436 return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1437 }
1438 return super.findByTitle(config);
1439 }
1440
1441 @Override
1442 public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1443 List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<SpecimenOrObservationBase<?>>();
1444 Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1445 if(fieldUnits.isEmpty()){
1446 allHierarchyDerivatives.add(specimen);
1447 allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1448 }
1449 else{
1450 for (FieldUnit fieldUnit : fieldUnits) {
1451 allHierarchyDerivatives.add(fieldUnit);
1452 allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1453 }
1454 }
1455 return allHierarchyDerivatives;
1456 }
1457
1458 @Override
1459 public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1460 List<DerivedUnit> childDerivate = new ArrayList<DerivedUnit>();
1461 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1462 for (DerivationEvent derivationEvent : derivationEvents) {
1463 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1464 for (DerivedUnit derivedUnit : derivatives) {
1465 childDerivate.add(derivedUnit);
1466 childDerivate.addAll(getAllChildDerivatives(derivedUnit));
1467 }
1468 }
1469 return childDerivate;
1470 }
1471
1472 @Override
1473 public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1474 if (config instanceof FindOccurrencesConfigurator) {
1475 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1476 Taxon taxon = null;
1477 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1478 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1479 if(taxonBase.isInstanceOf(Taxon.class)){
1480 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1481 }
1482 }
1483 // indirectly associated specimens
1484 if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1485 return findByTitle(config).getRecords().size();
1486 }
1487 return dao.countOccurrences(occurrenceConfig.getClazz(), occurrenceConfig.getTitleSearchString(),
1488 occurrenceConfig.getSignificantIdentifier(), occurrenceConfig.getSpecimenType(), taxon,
1489 occurrenceConfig.getMatchMode(), null, null, occurrenceConfig.getOrderHints(),
1490 occurrenceConfig.getPropertyPaths());
1491 }
1492 return super.countByTitle(config);
1493 }
1494
1495 }