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