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