Merge branch 'develop' into feature/cdm-4.7
[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.FindOccurrencesConfigurator.AssignmentStatus;
50 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
51 import eu.etaxonomy.cdm.api.service.config.SpecimenDeleteConfigurator;
52 import eu.etaxonomy.cdm.api.service.dto.DerivateDTO;
53 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO;
54 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.ContigFile;
55 import eu.etaxonomy.cdm.api.service.dto.DerivateDataDTO.Link;
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.LuceneParseException;
66 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
67 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
68 import eu.etaxonomy.cdm.api.service.search.SearchResult;
69 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
70 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
71 import eu.etaxonomy.cdm.common.CdmUtils;
72 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
73 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
74 import eu.etaxonomy.cdm.model.CdmBaseType;
75 import eu.etaxonomy.cdm.model.agent.AgentBase;
76 import eu.etaxonomy.cdm.model.common.CdmBase;
77 import eu.etaxonomy.cdm.model.common.DefinedTerm;
78 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
79 import eu.etaxonomy.cdm.model.common.ICdmBase;
80 import eu.etaxonomy.cdm.model.common.Language;
81 import eu.etaxonomy.cdm.model.description.CategoricalData;
82 import eu.etaxonomy.cdm.model.description.DescriptionBase;
83 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
84 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
85 import eu.etaxonomy.cdm.model.description.QuantitativeData;
86 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
87 import eu.etaxonomy.cdm.model.description.TaxonDescription;
88 import eu.etaxonomy.cdm.model.location.Country;
89 import eu.etaxonomy.cdm.model.location.NamedArea;
90 import eu.etaxonomy.cdm.model.media.Media;
91 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
92 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
93 import eu.etaxonomy.cdm.model.media.MediaUtils;
94 import eu.etaxonomy.cdm.model.molecular.AmplificationResult;
95 import eu.etaxonomy.cdm.model.molecular.DnaSample;
96 import eu.etaxonomy.cdm.model.molecular.Sequence;
97 import eu.etaxonomy.cdm.model.molecular.SingleRead;
98 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
99 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
100 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
101 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
102 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
103 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
104 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
105 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
106 import eu.etaxonomy.cdm.model.occurrence.MediaSpecimen;
107 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
108 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
109 import eu.etaxonomy.cdm.model.taxon.Taxon;
110 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
111 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
112 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
113 import eu.etaxonomy.cdm.persistence.dao.molecular.ISingleReadDao;
114 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
115 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
116 import eu.etaxonomy.cdm.persistence.query.OrderHint;
117 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
118 import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
119
120 /**
121 * @author a.babadshanjan
122 * @created 01.09.2008
123 */
124 @Service
125 @Transactional(readOnly = true)
126 public class OccurrenceServiceImpl extends IdentifiableServiceBase<SpecimenOrObservationBase, IOccurrenceDao> implements IOccurrenceService {
127
128 static private final Logger logger = Logger.getLogger(OccurrenceServiceImpl.class);
129
130 @Autowired
131 private IDefinedTermDao definedTermDao;
132
133 @Autowired
134 private IDescriptionService descriptionService;
135
136 @Autowired
137 private INameService nameService;
138
139 @Autowired
140 private ITaxonService taxonService;
141
142 @Autowired
143 private ISequenceService sequenceService;
144
145 @Autowired
146 private ISingleReadDao singleReadDao;
147
148 @Autowired
149 private AbstractBeanInitializer beanInitializer;
150
151 @Autowired
152 private ILuceneIndexToolProvider luceneIndexToolProvider;
153
154 private static final String SEPARATOR_STRING = ", ";
155
156 public OccurrenceServiceImpl() {
157 logger.debug("Load OccurrenceService Bean");
158 }
159
160
161 @Override
162 @Transactional(readOnly = false)
163 public void updateTitleCache(Class<? extends SpecimenOrObservationBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<SpecimenOrObservationBase> cacheStrategy, IProgressMonitor monitor) {
164 if (clazz == null) {
165 clazz = SpecimenOrObservationBase.class;
166 }
167 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
168 }
169
170 /**
171 * FIXME Candidate for harmonization
172 * move to termService
173 */
174 @Override
175 public Country getCountryByIso(String iso639) {
176 return this.definedTermDao.getCountryByIso(iso639);
177
178 }
179
180 /**
181 * FIXME Candidate for harmonization
182 * move to termService
183 */
184 @Override
185 public List<Country> getCountryByName(String name) {
186 List<? extends DefinedTermBase> terms = this.definedTermDao.findByTitle(Country.class, name, null, null, null, null, null, null);
187 List<Country> countries = new ArrayList<>();
188 for (int i = 0; i < terms.size(); i++) {
189 countries.add((Country) terms.get(i));
190 }
191 return countries;
192 }
193
194 @Override
195 @Autowired
196 protected void setDao(IOccurrenceDao dao) {
197 this.dao = dao;
198 }
199
200 @Override
201 public Pager<DerivationEvent> getDerivationEvents(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
202 Integer numberOfResults = dao.countDerivationEvents(occurence);
203
204 List<DerivationEvent> results = new ArrayList<>();
205 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
206 results = dao.getDerivationEvents(occurence, pageSize, pageNumber, propertyPaths);
207 }
208
209 return new DefaultPagerImpl<DerivationEvent>(pageNumber, numberOfResults, pageSize, results);
210 }
211
212 @Override
213 public int countDeterminations(SpecimenOrObservationBase occurence, TaxonBase taxonbase) {
214 return dao.countDeterminations(occurence, taxonbase);
215 }
216
217 @Override
218 public Pager<DeterminationEvent> getDeterminations(SpecimenOrObservationBase occurrence, TaxonBase taxonBase, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
219 Integer numberOfResults = dao.countDeterminations(occurrence, taxonBase);
220
221 List<DeterminationEvent> results = new ArrayList<>();
222 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
223 results = dao.getDeterminations(occurrence, taxonBase, pageSize, pageNumber, propertyPaths);
224 }
225
226 return new DefaultPagerImpl<DeterminationEvent>(pageNumber, numberOfResults, pageSize, results);
227 }
228
229 @Override
230 public Pager<Media> getMedia(SpecimenOrObservationBase occurence, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
231 Integer numberOfResults = dao.countMedia(occurence);
232
233 List<Media> results = new ArrayList<>();
234 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
235 results = dao.getMedia(occurence, pageSize, pageNumber, propertyPaths);
236 }
237
238 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
239 }
240
241 @Override
242 public Pager<Media> getMediainHierarchy(SpecimenOrObservationBase rootOccurence, Integer pageSize,
243 Integer pageNumber, List<String> propertyPaths) {
244 List<Media> media = new ArrayList<>();
245 //media specimens
246 if(rootOccurence.isInstanceOf(MediaSpecimen.class)){
247 MediaSpecimen mediaSpecimen = HibernateProxyHelper.deproxy(rootOccurence, MediaSpecimen.class);
248 media.add(mediaSpecimen.getMediaSpecimen());
249 }
250 // pherograms & gelPhotos
251 if (rootOccurence.isInstanceOf(DnaSample.class)) {
252 DnaSample dnaSample = CdmBase.deproxy(rootOccurence, DnaSample.class);
253 Set<Sequence> sequences = dnaSample.getSequences();
254 //we do show only those gelPhotos which lead to a consensus sequence
255 for (Sequence sequence : sequences) {
256 Set<Media> dnaRelatedMedia = new HashSet<>();
257 for (SingleRead singleRead : sequence.getSingleReads()){
258 AmplificationResult amplification = singleRead.getAmplificationResult();
259 dnaRelatedMedia.add(amplification.getGelPhoto());
260 dnaRelatedMedia.add(singleRead.getPherogram());
261 dnaRelatedMedia.remove(null);
262 }
263 media.addAll(dnaRelatedMedia);
264 }
265 }
266 if(rootOccurence.isInstanceOf(DerivedUnit.class)){
267 DerivedUnit derivedUnit = HibernateProxyHelper.deproxy(rootOccurence, DerivedUnit.class);
268 for (DerivationEvent derivationEvent : derivedUnit.getDerivationEvents()) {
269 for (DerivedUnit childDerivative : derivationEvent.getDerivatives()) {
270 media.addAll(getMediainHierarchy(childDerivative, pageSize, pageNumber, propertyPaths).getRecords());
271 }
272 }
273 }
274 return new DefaultPagerImpl<Media>(pageNumber, media.size(), pageSize, media);
275 }
276
277 @Override
278 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonNameBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
279 Integer numberOfResults = dao.count(type, determinedAs);
280 List<SpecimenOrObservationBase> results = new ArrayList<>();
281 pageNumber = pageNumber == null ? 0 : pageNumber;
282 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
283 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
284 results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
285 }
286 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
287 }
288
289 @Override
290 public Pager<SpecimenOrObservationBase> list(Class<? extends SpecimenOrObservationBase> type, TaxonBase determinedAs, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
291 Integer numberOfResults = dao.count(type, determinedAs);
292 List<SpecimenOrObservationBase> results = new ArrayList<>();
293 pageNumber = pageNumber == null ? 0 : pageNumber;
294 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
295 Integer start = pageSize == null ? 0 : pageSize * pageNumber;
296 results = dao.list(type, determinedAs, pageSize, start, orderHints, propertyPaths);
297 }
298 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, numberOfResults, pageSize, results);
299 }
300
301 @Override
302 public List<UuidAndTitleCache<DerivedUnit>> getDerivedUnitUuidAndTitleCache() {
303 return dao.getDerivedUnitUuidAndTitleCache();
304 }
305
306 @Override
307 public List<UuidAndTitleCache<FieldUnit>> getFieldUnitUuidAndTitleCache() {
308 return dao.getFieldUnitUuidAndTitleCache();
309 }
310
311 @Override
312 public DerivedUnitFacade getDerivedUnitFacade(DerivedUnit derivedUnit, List<String> propertyPaths) throws DerivedUnitFacadeNotSupportedException {
313 derivedUnit = (DerivedUnit) dao.load(derivedUnit.getUuid(), null);
314 DerivedUnitFacadeConfigurator config = DerivedUnitFacadeConfigurator.NewInstance();
315 config.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
316 DerivedUnitFacade derivedUnitFacade = DerivedUnitFacade.NewInstance(derivedUnit, config);
317 beanInitializer.initialize(derivedUnitFacade, propertyPaths);
318 return derivedUnitFacade;
319 }
320
321 @Override
322 public List<DerivedUnitFacade> listDerivedUnitFacades(
323 DescriptionBase description, List<String> propertyPaths) {
324
325 List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<>();
326 IndividualsAssociation tempIndividualsAssociation;
327 SpecimenOrObservationBase tempSpecimenOrObservationBase;
328 List<IndividualsAssociation> elements = descriptionService.listDescriptionElements(description, null, IndividualsAssociation.class, null, 0, Arrays.asList(new String []{"associatedSpecimenOrObservation"}));
329 for (IndividualsAssociation element : elements) {
330 tempIndividualsAssociation = HibernateProxyHelper.deproxy(element, IndividualsAssociation.class);
331 if (tempIndividualsAssociation.getAssociatedSpecimenOrObservation() != null) {
332 tempSpecimenOrObservationBase = HibernateProxyHelper.deproxy(tempIndividualsAssociation.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase.class);
333 if (tempSpecimenOrObservationBase.isInstanceOf(DerivedUnit.class)) {
334 try {
335 derivedUnitFacadeList.add(DerivedUnitFacade.NewInstance(HibernateProxyHelper.deproxy(tempSpecimenOrObservationBase, DerivedUnit.class)));
336 } catch (DerivedUnitFacadeNotSupportedException e) {
337 logger.warn(tempIndividualsAssociation.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e.getMessage());
338 }
339 }
340 }
341 }
342
343 beanInitializer.initializeAll(derivedUnitFacadeList, propertyPaths);
344
345 return derivedUnitFacadeList;
346 }
347
348
349 @Override
350 public <T extends SpecimenOrObservationBase> List<T> listByAssociatedTaxon(Class<T> type, Set<TaxonRelationshipEdge> includeRelationships,
351 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
352
353 return pageByAssociatedTaxon(type, includeRelationships, associatedTaxon, maxDepth, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
354 }
355
356 @Override
357 public Collection<SpecimenOrObservationBase> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon, List<OrderHint> orderHints, List<String> propertyPaths) {
358 return pageFieldUnitsByAssociatedTaxon(null, associatedTaxon, null, null, null, null, propertyPaths).getRecords();
359 }
360
361 @Override
362 public Pager<SpecimenOrObservationBase> pageFieldUnitsByAssociatedTaxon(Set<TaxonRelationshipEdge> includeRelationships,
363 Taxon associatedTaxon, Integer maxDepth, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
364 List<String> propertyPaths) {
365
366 if (!getSession().contains(associatedTaxon)) {
367 associatedTaxon = (Taxon) taxonService.load(associatedTaxon.getUuid());
368 }
369
370 // gather the IDs of all relevant field units
371 Set<UUID> fieldUnitUuids = new HashSet<>();
372 List<SpecimenOrObservationBase> records = listByAssociatedTaxon(null, includeRelationships, associatedTaxon, maxDepth, null, null, orderHints, propertyPaths);
373 for (SpecimenOrObservationBase<?> specimen : records) {
374 for (FieldUnit fieldUnit : getFieldUnits(specimen.getUuid())) {
375 fieldUnitUuids.add(fieldUnit.getUuid());
376 }
377 }
378 //dao.list() does the paging of the field units. Passing the field units directly to the Pager would not work
379 List<SpecimenOrObservationBase> fieldUnits = dao.list(fieldUnitUuids, pageSize, pageNumber, orderHints, propertyPaths);
380 return new DefaultPagerImpl<SpecimenOrObservationBase>(pageNumber, fieldUnitUuids.size(), pageSize, fieldUnits);
381 }
382
383 @Override
384 public FieldUnitDTO assembleFieldUnitDTO(FieldUnit fieldUnit, UUID associatedTaxonUuid) {
385
386 if (!getSession().contains(fieldUnit)) {
387 fieldUnit = (FieldUnit) load(fieldUnit.getUuid());
388 }
389 TaxonBase associatedTaxon = taxonService.load(associatedTaxonUuid);
390
391 FieldUnitDTO fieldUnitDTO = new FieldUnitDTO();
392
393 if (fieldUnit.getGatheringEvent() != null) {
394 GatheringEvent gatheringEvent = fieldUnit.getGatheringEvent();
395 // Country
396 NamedArea country = gatheringEvent.getCountry();
397 fieldUnitDTO.setCountry(country != null ? country.getDescription() : null);
398 // Collection
399 AgentBase collector = gatheringEvent.getCollector();
400 String fieldNumber = fieldUnit.getFieldNumber();
401 String collectionString = "";
402 if (collector != null || fieldNumber != null) {
403 collectionString += collector != null ? collector : "";
404 if (!collectionString.isEmpty()) {
405 collectionString += " ";
406 }
407 collectionString += (fieldNumber != null ? fieldNumber : "");
408 collectionString.trim();
409 }
410 fieldUnitDTO.setCollection(collectionString);
411 // Date
412 Partial gatheringDate = gatheringEvent.getGatheringDate();
413 String dateString = null;
414 if (gatheringDate != null) {
415 gatheringDate.toString();
416 }
417 else if(gatheringEvent.getTimeperiod()!=null && gatheringEvent.getTimeperiod().getFreeText()!=null){
418 dateString = gatheringEvent.getTimeperiod().getFreeText();
419 }
420 fieldUnitDTO.setDate(dateString);
421 }
422
423 // Taxon Name
424 fieldUnitDTO.setTaxonName(associatedTaxon.getName().getTitleCache());
425
426 // Herbaria map
427 Map<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> collectionToCountMap = new HashMap<>();
428 // List of accession numbers for citation
429 List<String> preservedSpecimenAccessionNumbers = new ArrayList<>();
430
431 // assemble preserved specimen DTOs
432 Set<DerivationEvent> derivationEvents = fieldUnit.getDerivationEvents();
433 for (DerivationEvent derivationEvent : derivationEvents) {
434 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
435 for (DerivedUnit derivedUnit : derivatives) {
436 if(!derivedUnit.isPublish()){
437 continue;
438 }
439 // collect accession numbers for citation
440 String mostSignificantIdentifier = getMostSignificantIdentifier(derivedUnit);
441 if (mostSignificantIdentifier != null) {
442 preservedSpecimenAccessionNumbers.add(mostSignificantIdentifier);
443 }
444 // collect collections for herbaria column
445 if (derivedUnit.getCollection() != null) {
446 Integer herbariumCount = collectionToCountMap.get(derivedUnit.getCollection());
447 if (herbariumCount == null) {
448 herbariumCount = 0;
449 }
450 collectionToCountMap.put(derivedUnit.getCollection(), herbariumCount + 1);
451 }
452 if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen)) {
453 PreservedSpecimenDTO preservedSpecimenDTO = assemblePreservedSpecimenDTO(derivedUnit, fieldUnitDTO);
454 fieldUnitDTO.addPreservedSpecimenDTO(preservedSpecimenDTO);
455 fieldUnitDTO.setHasCharacterData(fieldUnitDTO.isHasCharacterData() || preservedSpecimenDTO.isHasCharacterData());
456 fieldUnitDTO.setHasDetailImage(fieldUnitDTO.isHasDetailImage() || preservedSpecimenDTO.isHasDetailImage());
457 fieldUnitDTO.setHasDna(fieldUnitDTO.isHasDna() || preservedSpecimenDTO.isHasDna());
458 fieldUnitDTO.setHasSpecimenScan(fieldUnitDTO.isHasSpecimenScan() || preservedSpecimenDTO.isHasSpecimenScan());
459 }
460 }
461 }
462 // assemble derivate data DTO
463 assembleDerivateDataDTO(fieldUnitDTO, fieldUnit);
464
465 // assemble citation
466 String citation = fieldUnit.getTitleCache();
467 if((CdmUtils.isBlank(citation) || citation.equals(IdentifiableEntityDefaultCacheStrategy.TITLE_CACHE_GENERATION_NOT_IMPLEMENTED))
468 && !fieldUnit.isProtectedTitleCache()){
469 fieldUnit.setTitleCache(null);
470 citation = fieldUnit.getTitleCache();
471 }
472 if (!preservedSpecimenAccessionNumbers.isEmpty()) {
473 citation += " (";
474 for (String accessionNumber : preservedSpecimenAccessionNumbers) {
475 if (!accessionNumber.isEmpty()) {
476 citation += accessionNumber + SEPARATOR_STRING;
477 }
478 }
479 citation = removeTail(citation, SEPARATOR_STRING);
480 citation += ")";
481 }
482 fieldUnitDTO.setCitation(citation);
483
484 // assemble herbaria string
485 String herbariaString = "";
486 for (Entry<eu.etaxonomy.cdm.model.occurrence.Collection, Integer> e : collectionToCountMap.entrySet()) {
487 eu.etaxonomy.cdm.model.occurrence.Collection collection = e.getKey();
488 if (collection.getCode() != null) {
489 herbariaString += collection.getCode();
490 }
491 if (e.getValue() > 1) {
492 herbariaString += "(" + e.getValue() + ")";
493 }
494 herbariaString += SEPARATOR_STRING;
495 }
496 herbariaString = removeTail(herbariaString, SEPARATOR_STRING);
497 fieldUnitDTO.setHerbarium(herbariaString);
498
499 return fieldUnitDTO;
500 }
501
502 @Override
503 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit) {
504 return assemblePreservedSpecimenDTO(derivedUnit, null);
505 }
506
507 @Override
508 public String getMostSignificantIdentifier(DerivedUnit derivedUnit) {
509 if (derivedUnit.getAccessionNumber() != null && !derivedUnit.getAccessionNumber().isEmpty()) {
510 return derivedUnit.getAccessionNumber();
511 }
512 else if(derivedUnit.getBarcode()!=null && !derivedUnit.getBarcode().isEmpty()){
513 return derivedUnit.getBarcode();
514 }
515 else if(derivedUnit.getCatalogNumber()!=null && !derivedUnit.getCatalogNumber().isEmpty()){
516 return derivedUnit.getCatalogNumber();
517 }
518 return null;
519 }
520
521 public PreservedSpecimenDTO assemblePreservedSpecimenDTO(DerivedUnit derivedUnit, FieldUnitDTO fieldUnitDTO) {
522 if (!getSession().contains(derivedUnit)) {
523 derivedUnit = (DerivedUnit) load(derivedUnit.getUuid());
524 }
525 PreservedSpecimenDTO preservedSpecimenDTO = new PreservedSpecimenDTO();
526
527 // check identifiers in priority order accNo>barCode>catalogNumber>collection
528 String identifier = derivedUnit.getMostSignificantIdentifier();
529 if(CdmUtils.isBlank(identifier) && derivedUnit.getCollection()!=null){
530 identifier = derivedUnit.getCollection().toString();
531 }
532 if(CdmUtils.isBlank(identifier)){
533 identifier = derivedUnit.getTitleCache();
534 if(CdmUtils.isBlank(identifier) && !derivedUnit.isProtectedTitleCache()){
535 //regenerate title cache if it is empty
536 derivedUnit.setTitleCache(null);
537 identifier = derivedUnit.getTitleCache();
538 }
539 }
540 if(CdmUtils.isBlank(identifier)){
541 //default fallback UUID
542 identifier = derivedUnit.getUuid().toString();
543 }
544 preservedSpecimenDTO.setAccessionNumber(identifier);
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<TaxonNameBase> typifiedNames = specimenTypeDesignation.getTypifiedNames();
593 for (TaxonNameBase taxonNameBase : typifiedNames) {
594 typedTaxaNames.add(taxonNameBase.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 TaxonNameBase
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 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 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 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("Derivative 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 if (config.isDeleteChildren()) {
1210 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1211 //clone to avoid concurrent modification
1212 //can happen if the child is deleted and deleted its own derivedFrom event
1213 Set<DerivationEvent> derivationEventsClone = new HashSet<>(derivationEvents);
1214 for (DerivationEvent derivationEvent : derivationEventsClone) {
1215 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1216 Iterator<DerivedUnit> it = derivatives.iterator();
1217 Set<DerivedUnit> derivativesToDelete = new HashSet<>();
1218 while (it.hasNext()) {
1219 DerivedUnit unit = it.next();
1220 derivativesToDelete.add(unit);
1221 }
1222 for (DerivedUnit unit:derivativesToDelete){
1223 delete(unit, config);
1224 }
1225 }
1226 }
1227
1228
1229
1230 DeleteResult deleteResult = isDeletable(specimen.getUuid(), config);
1231 if (!deleteResult.isOk()) {
1232 return deleteResult;
1233 }
1234
1235 // check related objects
1236 Set<CdmBase> relatedObjects = deleteResult.getRelatedObjects();
1237
1238 for (CdmBase relatedObject : relatedObjects) {
1239 // check for TypeDesignations
1240 if (relatedObject.isInstanceOf(SpecimenTypeDesignation.class)) {
1241 SpecimenTypeDesignation designation = HibernateProxyHelper.deproxy(relatedObject, SpecimenTypeDesignation.class);
1242 designation.setTypeSpecimen(null);
1243 List<TaxonNameBase> typifiedNames = new ArrayList<>();
1244 typifiedNames.addAll(designation.getTypifiedNames());
1245 for (TaxonNameBase taxonNameBase : typifiedNames) {
1246 taxonNameBase.removeTypeDesignation(designation);
1247 }
1248 }
1249 // delete IndividualsAssociation
1250 if (relatedObject.isInstanceOf(IndividualsAssociation.class)) {
1251 IndividualsAssociation association = HibernateProxyHelper.deproxy(relatedObject, IndividualsAssociation.class);
1252 association.setAssociatedSpecimenOrObservation(null);
1253 association.getInDescription().removeElement(association);
1254 }
1255 // check for "described specimen" (deprecated)
1256 if (relatedObject.isInstanceOf(TaxonDescription.class)) {
1257 TaxonDescription description = HibernateProxyHelper.deproxy(relatedObject, TaxonDescription.class);
1258 description.setDescribedSpecimenOrObservation(null);
1259 }
1260 // check for specimen description
1261 if (relatedObject.isInstanceOf(SpecimenDescription.class)) {
1262 SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(relatedObject, SpecimenDescription.class);
1263 specimenDescription.setDescribedSpecimenOrObservation(null);
1264 // check if description is a description of the given specimen
1265 if (specimen.getDescriptions().contains(specimenDescription)) {
1266 specimen.removeDescription(specimenDescription);
1267 }
1268 DeleteResult descriptionDelete = descriptionService.isDeletable(specimenDescription.getUuid(), null);
1269 if (descriptionDelete.isOk()){
1270 descriptionService.delete(specimenDescription);
1271 }
1272 }
1273 // check for amplification
1274 if (relatedObject.isInstanceOf(AmplificationResult.class)) {
1275 AmplificationResult amplificationResult = HibernateProxyHelper.deproxy(relatedObject, AmplificationResult.class);
1276 amplificationResult.getDnaSample().removeAmplificationResult(amplificationResult);
1277 }
1278 // check for sequence
1279 if (relatedObject.isInstanceOf(Sequence.class)) {
1280 Sequence sequence = HibernateProxyHelper.deproxy(relatedObject, Sequence.class);
1281 sequence.getDnaSample().removeSequence(sequence);
1282 }
1283 // check for children and parents (derivation events)
1284 if (relatedObject.isInstanceOf(DerivationEvent.class)) {
1285 DerivationEvent derivationEvent = HibernateProxyHelper.deproxy(relatedObject, DerivationEvent.class);
1286 // parent derivation event (derivedFrom)
1287 if (derivationEvent.getDerivatives().contains(specimen) && specimen.isInstanceOf(DerivedUnit.class)) {
1288 derivationEvent.removeDerivative(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
1289 if (derivationEvent.getDerivatives().isEmpty()) {
1290 Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
1291 for (SpecimenOrObservationBase specimenOrObservationBase : originals) {
1292 specimenOrObservationBase.removeDerivationEvent(derivationEvent);
1293 deleteResult.addUpdatedObject(specimenOrObservationBase);
1294 }
1295 }
1296 }
1297 else{
1298 //child derivation events should not occur since we delete the hierarchy from bottom to top
1299 }
1300 }
1301 }
1302 if (specimen instanceof FieldUnit){
1303 FieldUnit fieldUnit = HibernateProxyHelper.deproxy(specimen, FieldUnit.class);
1304 GatheringEvent event = fieldUnit.getGatheringEvent();
1305 fieldUnit.setGatheringEvent(null);
1306
1307
1308 }
1309 deleteResult.includeResult(delete(specimen));
1310
1311 return deleteResult;
1312 }
1313
1314 @Override
1315 public Collection<IndividualsAssociation> listIndividualsAssociations(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1316 return dao.listIndividualsAssociations(specimen, null, null, null, null);
1317 }
1318
1319 @Override
1320 public Collection<TaxonBase<?>> listAssociatedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1321 List<OrderHint> orderHints, List<String> propertyPaths) {
1322 Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1323
1324 //individuals associations
1325 associatedTaxa.addAll(listIndividualsAssociationTaxa(specimen, limit, start, orderHints, propertyPaths));
1326 //type designation
1327 if(specimen.isInstanceOf(DerivedUnit.class)){
1328 associatedTaxa.addAll(listTypeDesignationTaxa(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class), limit, start, orderHints, propertyPaths));
1329 }
1330 //determinations
1331 associatedTaxa.addAll(listDeterminedTaxa(specimen, limit, start, orderHints, propertyPaths));
1332
1333 return associatedTaxa;
1334 }
1335
1336
1337 @Override
1338 public Collection<TaxonBase<?>> listDeterminedTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1339 List<OrderHint> orderHints, List<String> propertyPaths) {
1340 Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1341 for (DeterminationEvent determinationEvent : listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths)) {
1342 if(determinationEvent.getIdentifiedUnit().equals(specimen)){
1343 if(determinationEvent.getTaxon()!=null){
1344 associatedTaxa.add(determinationEvent.getTaxon());
1345 }
1346 if(determinationEvent.getTaxonName()!=null){
1347 associatedTaxa.addAll(determinationEvent.getTaxonName().getTaxonBases());
1348 }
1349 }
1350 }
1351 return associatedTaxa;
1352 }
1353
1354 @Override
1355 public Collection<TaxonBase<?>> listTypeDesignationTaxa(DerivedUnit specimen, Integer limit, Integer start,
1356 List<OrderHint> orderHints, List<String> propertyPaths) {
1357 Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1358 for (SpecimenTypeDesignation typeDesignation : listTypeDesignations(specimen, limit, start, orderHints, propertyPaths)) {
1359 if(typeDesignation.getTypeSpecimen().equals(specimen)){
1360 Set<TaxonNameBase> typifiedNames = typeDesignation.getTypifiedNames();
1361 for (TaxonNameBase taxonNameBase : typifiedNames) {
1362 associatedTaxa.addAll(taxonNameBase.getTaxa());
1363 }
1364 }
1365 }
1366 return associatedTaxa;
1367 }
1368
1369 @Override
1370 public Collection<TaxonBase<?>> listIndividualsAssociationTaxa(SpecimenOrObservationBase<?> specimen, Integer limit, Integer start,
1371 List<OrderHint> orderHints, List<String> propertyPaths) {
1372 Collection<TaxonBase<?>> associatedTaxa = new HashSet<>();
1373 for (IndividualsAssociation individualsAssociation : listIndividualsAssociations(specimen, limit, start, orderHints, propertyPaths)) {
1374 if(individualsAssociation.getInDescription().isInstanceOf(TaxonDescription.class)){
1375 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(individualsAssociation.getInDescription(), TaxonDescription.class);
1376 if(taxonDescription.getTaxon()!=null){
1377 associatedTaxa.add(taxonDescription.getTaxon());
1378 }
1379 }
1380 }
1381 return associatedTaxa;
1382 }
1383
1384 @Override
1385 public Collection<DeterminationEvent> listDeterminationEvents(SpecimenOrObservationBase<?> specimen,
1386 Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1387 return dao.listDeterminationEvents(specimen, limit, start, orderHints, propertyPaths);
1388 }
1389
1390 @Override
1391 public Map<DerivedUnit, Collection<SpecimenTypeDesignation>> listTypeDesignations(
1392 Collection<DerivedUnit> specimens, Integer limit, Integer start,
1393 List<OrderHint> orderHints, List<String> propertyPaths) {
1394 Map<DerivedUnit, Collection<SpecimenTypeDesignation>> typeDesignationMap = new HashMap<>();
1395 for (DerivedUnit specimen : specimens) {
1396 Collection<SpecimenTypeDesignation> typeDesignations = listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1397 typeDesignationMap.put(specimen, typeDesignations);
1398 }
1399 return typeDesignationMap;
1400 }
1401
1402 @Override
1403 public Collection<SpecimenTypeDesignation> listTypeDesignations(DerivedUnit specimen,
1404 Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1405 return dao.listTypeDesignations(specimen, limit, start, orderHints, propertyPaths);
1406 }
1407
1408 @Override
1409 public Collection<DescriptionBase<?>> listDescriptionsWithDescriptionSpecimen(
1410 SpecimenOrObservationBase<?> specimen, Integer limit, Integer start, List<OrderHint> orderHints,
1411 List<String> propertyPaths) {
1412 return dao.listDescriptionsWithDescriptionSpecimen(specimen, limit, start, orderHints, propertyPaths);
1413 }
1414
1415 @Override
1416 @Deprecated //this is not a service layer task so it may be removed in future versions
1417 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(SpecimenOrObservationBase<?> specimen) {
1418 if (specimen != null) {
1419 return specimen.characterData();
1420 }else{
1421 return new ArrayList<>();
1422 }
1423 }
1424
1425 @Override
1426 public Collection<DescriptionElementBase> getCharacterDataForSpecimen(UUID specimenUuid) {
1427 SpecimenOrObservationBase<?> specimen = load(specimenUuid);
1428 if (specimen != null) {
1429 return getCharacterDataForSpecimen(specimen);
1430 }
1431 else{
1432 throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1433 }
1434 }
1435
1436
1437 @Override
1438 public Integer countByTitle(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1439 if (config instanceof FindOccurrencesConfigurator) {
1440 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1441 Taxon taxon = null;
1442 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1443 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1444 if(taxonBase.isInstanceOf(Taxon.class)){
1445 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1446 }
1447 }
1448 TaxonNameBase taxonName = null;
1449 if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1450 taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1451 }
1452 return dao.countOccurrences(occurrenceConfig.getClazz(),
1453 occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1454 occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1455 occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths());
1456 }
1457 else{
1458 return dao.countByTitle(config.getTitleSearchString());
1459 }
1460
1461 }
1462
1463 @Override
1464 public Pager<SpecimenOrObservationBase> findByTitle(
1465 IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config) {
1466 if (config instanceof FindOccurrencesConfigurator) {
1467 FindOccurrencesConfigurator occurrenceConfig = (FindOccurrencesConfigurator) config;
1468 List<SpecimenOrObservationBase> occurrences = new ArrayList<>();
1469 Taxon taxon = null;
1470 if(occurrenceConfig.getAssociatedTaxonUuid()!=null){
1471 TaxonBase taxonBase = taxonService.load(occurrenceConfig.getAssociatedTaxonUuid());
1472 if(taxonBase.isInstanceOf(Taxon.class)){
1473 taxon = HibernateProxyHelper.deproxy(taxonBase, Taxon.class);
1474 }
1475 }
1476 TaxonNameBase taxonName = null;
1477 if(occurrenceConfig.getAssociatedTaxonNameUuid()!=null){
1478 taxonName = nameService.load(occurrenceConfig.getAssociatedTaxonNameUuid());
1479 }
1480 occurrences.addAll(dao.findOccurrences(occurrenceConfig.getClazz(),
1481 occurrenceConfig.getTitleSearchString(), occurrenceConfig.getSignificantIdentifier(),
1482 occurrenceConfig.getSpecimenType(), taxon, taxonName, occurrenceConfig.getMatchMode(), null, null,
1483 occurrenceConfig.getOrderHints(), occurrenceConfig.getPropertyPaths()));
1484 //filter out (un-)assigned specimens
1485 if(taxon==null && taxonName==null){
1486 AssignmentStatus assignmentStatus = occurrenceConfig.getAssignmentStatus();
1487 List<SpecimenOrObservationBase<?>> specimenWithAssociations = new ArrayList<>();
1488 if(!assignmentStatus.equals(AssignmentStatus.ALL_SPECIMENS)){
1489 for (SpecimenOrObservationBase specimenOrObservationBase : occurrences) {
1490 Collection<TaxonBase<?>> associatedTaxa = listAssociatedTaxa(specimenOrObservationBase, null, null, null, null);
1491 if(!associatedTaxa.isEmpty()){
1492 specimenWithAssociations.add(specimenOrObservationBase);
1493 }
1494 }
1495 }
1496 if(assignmentStatus.equals(AssignmentStatus.UNASSIGNED_SPECIMENS)){
1497 occurrences.removeAll(specimenWithAssociations);
1498 }
1499 if(assignmentStatus.equals(AssignmentStatus.ASSIGNED_SPECIMENS)){
1500 occurrences = new ArrayList<>(specimenWithAssociations);
1501 }
1502 }
1503 // indirectly associated specimens
1504 if(occurrenceConfig.isRetrieveIndirectlyAssociatedSpecimens()){
1505 List<SpecimenOrObservationBase> indirectlyAssociatedOccurrences = new ArrayList<>(occurrences);
1506 for (SpecimenOrObservationBase specimen : occurrences) {
1507 List<SpecimenOrObservationBase<?>> allHierarchyDerivates = getAllHierarchyDerivatives(specimen);
1508 for (SpecimenOrObservationBase<?> specimenOrObservationBase : allHierarchyDerivates) {
1509 if(!occurrences.contains(specimenOrObservationBase)){
1510 indirectlyAssociatedOccurrences.add(specimenOrObservationBase);
1511 }
1512 }
1513 }
1514 occurrences = indirectlyAssociatedOccurrences;
1515 }
1516
1517 return new DefaultPagerImpl<SpecimenOrObservationBase>(config.getPageNumber(), occurrences.size(), config.getPageSize(), occurrences);
1518 }
1519 return super.findByTitle(config);
1520 }
1521
1522 @Override
1523 public List<SpecimenOrObservationBase<?>> getAllHierarchyDerivatives(SpecimenOrObservationBase<?> specimen){
1524 List<SpecimenOrObservationBase<?>> allHierarchyDerivatives = new ArrayList<>();
1525 Collection<FieldUnit> fieldUnits = getFieldUnits(specimen.getUuid());
1526 if(fieldUnits.isEmpty()){
1527 allHierarchyDerivatives.add(specimen);
1528 allHierarchyDerivatives.addAll(getAllChildDerivatives(specimen));
1529 }
1530 else{
1531 for (FieldUnit fieldUnit : fieldUnits) {
1532 allHierarchyDerivatives.add(fieldUnit);
1533 allHierarchyDerivatives.addAll(getAllChildDerivatives(fieldUnit));
1534 }
1535 }
1536 return allHierarchyDerivatives;
1537 }
1538
1539 @Override
1540 public List<DerivedUnit> getAllChildDerivatives(UUID specimenUuid){
1541 return getAllChildDerivatives(load(specimenUuid));
1542 }
1543
1544 @Override
1545 public List<DerivedUnit> getAllChildDerivatives(SpecimenOrObservationBase<?> specimen){
1546 if (specimen == null){
1547 return null;
1548 }
1549 List<DerivedUnit> childDerivate = new ArrayList<>();
1550 Set<DerivationEvent> derivationEvents = specimen.getDerivationEvents();
1551 for (DerivationEvent derivationEvent : derivationEvents) {
1552 Set<DerivedUnit> derivatives = derivationEvent.getDerivatives();
1553 for (DerivedUnit derivedUnit : derivatives) {
1554 childDerivate.add(derivedUnit);
1555 childDerivate.addAll(getAllChildDerivatives(derivedUnit.getUuid()));
1556 }
1557 }
1558 return childDerivate;
1559 }
1560
1561 @Override
1562 public int countOccurrences(IIdentifiableEntityServiceConfigurator<SpecimenOrObservationBase> config){
1563 return countByTitle(config);
1564 }
1565
1566 }