2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.api
.service
;
11 import java
.io
.IOException
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Arrays
;
14 import java
.util
.Collection
;
15 import java
.util
.Collections
;
16 import java
.util
.Comparator
;
17 import java
.util
.EnumSet
;
18 import java
.util
.HashMap
;
19 import java
.util
.HashSet
;
20 import java
.util
.Iterator
;
21 import java
.util
.List
;
24 import java
.util
.UUID
;
25 import java
.util
.stream
.Collectors
;
27 import org
.apache
.commons
.lang3
.StringUtils
;
28 import org
.apache
.logging
.log4j
.LogManager
;
29 import org
.apache
.logging
.log4j
.Logger
;
30 import org
.apache
.lucene
.queryparser
.classic
.ParseException
;
31 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
32 import org
.apache
.lucene
.search
.BooleanQuery
.Builder
;
33 import org
.apache
.lucene
.search
.SortField
;
34 import org
.apache
.lucene
.search
.grouping
.TopGroups
;
35 import org
.apache
.lucene
.util
.BytesRef
;
36 import org
.hibernate
.TransientObjectException
;
37 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
38 import org
.springframework
.dao
.DataRetrievalFailureException
;
39 import org
.springframework
.stereotype
.Service
;
40 import org
.springframework
.transaction
.annotation
.Transactional
;
42 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.FindOccurrencesConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.IIdentifiableEntityServiceConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.SpecimenDeleteConfigurator
;
47 import eu
.etaxonomy
.cdm
.api
.service
.dto
.DNASampleDTO
;
48 import eu
.etaxonomy
.cdm
.api
.service
.dto
.DerivedUnitDTO
;
49 import eu
.etaxonomy
.cdm
.api
.service
.dto
.FieldUnitDTO
;
50 import eu
.etaxonomy
.cdm
.api
.service
.dto
.MediaDTO
;
51 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RectangleDTO
;
52 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenOrObservationBaseDTO
;
53 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenOrObservationDTOFactory
;
54 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
55 import eu
.etaxonomy
.cdm
.api
.service
.molecular
.ISequenceService
;
56 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
57 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
58 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
61 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneParseException
;
62 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
63 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
64 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
65 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
66 import eu
.etaxonomy
.cdm
.api
.util
.TaxonRelationshipEdge
;
67 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
68 import eu
.etaxonomy
.cdm
.compare
.common
.PartialComparator
;
69 import eu
.etaxonomy
.cdm
.facade
.DerivedUnitFacade
;
70 import eu
.etaxonomy
.cdm
.facade
.DerivedUnitFacadeConfigurator
;
71 import eu
.etaxonomy
.cdm
.facade
.DerivedUnitFacadeNotSupportedException
;
72 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
73 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
74 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
75 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
76 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
77 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
78 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
79 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
80 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
81 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
82 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
83 import eu
.etaxonomy
.cdm
.model
.molecular
.AmplificationResult
;
84 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
85 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
86 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
87 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
88 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
89 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
90 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
91 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
92 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
93 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
94 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
95 import eu
.etaxonomy
.cdm
.model
.occurrence
.MediaSpecimen
;
96 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
97 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
98 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
99 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
100 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
101 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
102 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
103 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
104 import eu
.etaxonomy
.cdm
.persistence
.query
.AssignmentStatus
;
105 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
106 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
109 * @author a.babadshanjan
113 @Transactional(readOnly
= true)
114 public class OccurrenceServiceImpl
115 extends IdentifiableServiceBase
<SpecimenOrObservationBase
, IOccurrenceDao
>
116 implements IOccurrenceService
{
118 private static final Logger logger
= LogManager
.getLogger();
121 private IDescriptionService descriptionService
;
124 private INameService nameService
;
127 private IEventBaseService eventService
;
130 private ITaxonService taxonService
;
133 private ISequenceService sequenceService
;
136 private AbstractBeanInitializer
<?
> beanInitializer
;
139 private ILuceneIndexToolProvider luceneIndexToolProvider
;
141 public OccurrenceServiceImpl() {
142 logger
.debug("Load OccurrenceService Bean");
146 @Transactional(readOnly
= false)
147 public UpdateResult
updateCaches(Class
<?
extends SpecimenOrObservationBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<SpecimenOrObservationBase
> cacheStrategy
, IProgressMonitor monitor
) {
149 clazz
= SpecimenOrObservationBase
.class;
151 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
156 protected void setDao(IOccurrenceDao dao
) {
161 public Pager
<DerivationEvent
> getDerivationEvents(SpecimenOrObservationBase occurence
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
162 long numberOfResults
= dao
.countDerivationEvents(occurence
);
164 List
<DerivationEvent
> results
= new ArrayList
<>();
165 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
166 results
= dao
.getDerivationEvents(occurence
, pageSize
, pageNumber
, propertyPaths
);
169 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
173 public long countDeterminations(SpecimenOrObservationBase occurence
, TaxonBase taxonbase
) {
174 return dao
.countDeterminations(occurence
, taxonbase
);
178 public Pager
<DeterminationEvent
> getDeterminations(SpecimenOrObservationBase occurrence
, TaxonBase taxonBase
,
179 Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
180 long numberOfResults
= dao
.countDeterminations(occurrence
, taxonBase
);
182 List
<DeterminationEvent
> results
= new ArrayList
<>();
183 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
184 results
= dao
.getDeterminations(occurrence
, taxonBase
, pageSize
, pageNumber
, propertyPaths
);
187 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
191 public Pager
<Media
> getMedia(SpecimenOrObservationBase occurence
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
192 long numberOfResults
= dao
.countMedia(occurence
);
194 List
<Media
> results
= new ArrayList
<>();
195 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
196 results
= dao
.getMedia(occurence
, pageSize
, pageNumber
, propertyPaths
);
199 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
203 public Pager
<MediaDTO
> getMediaDTOs(SpecimenOrObservationBase
<?
> occurence
, Integer pageSize
, Integer pageNumber
) {
204 long numberOfResults
= dao
.countMedia(occurence
);
206 List
<Media
> results
= new ArrayList
<>();
207 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
, pageNumber
, pageSize
)) {
208 results
= dao
.getMedia(occurence
, pageSize
, pageNumber
, null);
210 List
<MediaDTO
> mediaDTOs
= results
.stream()
211 .map(m
-> MediaDTO
.fromEntity(m
))
212 .flatMap(dtos
-> dtos
.stream())
213 .collect(Collectors
.toList()
215 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, mediaDTOs
);
218 public Pager
<Media
> getMediaInHierarchy(SpecimenOrObservationBase
<?
> rootOccurence
, boolean collectOriginalMedia
, boolean collectDerivateMedia
, Integer pageSize
,
219 Integer pageNumber
, List
<String
> propertyPaths
) {
220 List
<Media
> media
= new ArrayList
<>();
222 if(rootOccurence
.isInstanceOf(MediaSpecimen
.class)){
223 MediaSpecimen mediaSpecimen
= HibernateProxyHelper
.deproxy(rootOccurence
, MediaSpecimen
.class);
224 media
.add(mediaSpecimen
.getMediaSpecimen());
226 // pherograms & gelPhotos
227 if (rootOccurence
.isInstanceOf(DnaSample
.class)) {
228 DnaSample dnaSample
= CdmBase
.deproxy(rootOccurence
, DnaSample
.class);
229 Set
<Sequence
> sequences
= dnaSample
.getSequences();
230 //we do show only those gelPhotos which lead to a consensus sequence
231 for (Sequence sequence
: sequences
) {
232 Set
<Media
> dnaRelatedMedia
= new HashSet
<>();
233 for (SingleRead singleRead
: sequence
.getSingleReads()){
234 AmplificationResult amplification
= singleRead
.getAmplificationResult();
235 dnaRelatedMedia
.add(amplification
.getGelPhoto());
236 dnaRelatedMedia
.add(singleRead
.getPherogram());
237 dnaRelatedMedia
.remove(null);
239 media
.addAll(dnaRelatedMedia
);
242 if(rootOccurence
.isInstanceOf(DerivedUnit
.class)){
243 DerivedUnit derivedUnit
= HibernateProxyHelper
.deproxy(rootOccurence
, DerivedUnit
.class);
244 if (collectDerivateMedia
) {
245 for (DerivationEvent derivationEvent
: derivedUnit
.getDerivationEvents()) {
246 for (DerivedUnit childDerivative
: derivationEvent
.getDerivatives()) {
247 //collectOriginalMedia should only called for the first derived unit
248 media
.addAll(getMediaInHierarchy(childDerivative
, false, true, pageSize
, pageNumber
, propertyPaths
).getRecords());
252 if (collectOriginalMedia
) {
253 for (SpecimenOrObservationBase original
: derivedUnit
.getOriginals()) {
254 //collect media to the top of the tree
255 media
.addAll(getMediaInHierarchy(original
, true, false, pageSize
, pageNumber
, propertyPaths
).getRecords());
261 return new DefaultPagerImpl
<>(pageNumber
, Long
.valueOf(media
.size()), pageSize
, media
);
266 public Pager
<Media
> getMediaInHierarchy(SpecimenOrObservationBase
<?
> rootOccurence
, Integer pageSize
,
267 Integer pageNumber
, List
<String
> propertyPaths
) {
268 return getMediaInHierarchy(rootOccurence
, false, true, pageSize
,
269 pageNumber
, propertyPaths
);
274 public Pager
<SpecimenOrObservationBase
> list(Class
<?
extends SpecimenOrObservationBase
> type
, TaxonName determinedAs
,
275 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
276 long numberOfResults
= dao
.count(type
, determinedAs
);
277 @SuppressWarnings("rawtypes")
278 List
<SpecimenOrObservationBase
> results
= new ArrayList
<>();
279 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
280 results
= dao
.list(type
, determinedAs
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
282 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
286 public Pager
<SpecimenOrObservationBase
> list(Class
<?
extends SpecimenOrObservationBase
> type
, TaxonBase determinedAs
,
287 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
288 long numberOfResults
= dao
.count(type
, determinedAs
);
289 @SuppressWarnings("rawtypes")
290 List
<SpecimenOrObservationBase
> results
= new ArrayList
<>();
291 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
292 results
= dao
.list(type
, determinedAs
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
294 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
298 public List
<UuidAndTitleCache
<DerivedUnit
>> getDerivedUnitUuidAndTitleCache(Integer limit
, String pattern
) {
299 return dao
.getDerivedUnitUuidAndTitleCache(limit
, pattern
);
303 public List
<UuidAndTitleCache
<FieldUnit
>> getFieldUnitUuidAndTitleCache() {
304 return dao
.getFieldUnitUuidAndTitleCache();
308 public DerivedUnitFacade
getDerivedUnitFacade(DerivedUnit derivedUnit
, List
<String
> derivedUnitFacadeInitStrategy
) throws DerivedUnitFacadeNotSupportedException
{
309 derivedUnit
= (DerivedUnit
) dao
.load(derivedUnit
.getUuid(), null);
310 DerivedUnitFacadeConfigurator config
= DerivedUnitFacadeConfigurator
.NewInstance();
311 config
.setThrowExceptionForNonSpecimenPreservationMethodRequest(false);
312 DerivedUnitFacade derivedUnitFacade
= DerivedUnitFacade
.NewInstance(derivedUnit
, config
);
313 beanInitializer
.initialize(derivedUnitFacade
, derivedUnitFacadeInitStrategy
);
314 return derivedUnitFacade
;
318 public List
<DerivedUnitFacade
> listDerivedUnitFacades(
319 DescriptionBase description
, List
<String
> derivedUnitFacadeInitStrategy
) {
321 List
<DerivedUnitFacade
> derivedUnitFacadeList
= new ArrayList
<>();
322 IndividualsAssociation tempIndividualsAssociation
;
323 SpecimenOrObservationBase tempSpecimenOrObservationBase
;
324 List
<IndividualsAssociation
> elements
= descriptionService
.listDescriptionElements(description
, null, IndividualsAssociation
.class, null, 0, Arrays
.asList(new String
[]{"associatedSpecimenOrObservation"}));
325 for (IndividualsAssociation element
: elements
) {
326 tempIndividualsAssociation
= HibernateProxyHelper
.deproxy(element
, IndividualsAssociation
.class);
327 if (tempIndividualsAssociation
.getAssociatedSpecimenOrObservation() != null) {
328 tempSpecimenOrObservationBase
= HibernateProxyHelper
.deproxy(tempIndividualsAssociation
.getAssociatedSpecimenOrObservation(), SpecimenOrObservationBase
.class);
329 if (tempSpecimenOrObservationBase
.isInstanceOf(DerivedUnit
.class)) {
331 derivedUnitFacadeList
.add(DerivedUnitFacade
.NewInstance(HibernateProxyHelper
.deproxy(tempSpecimenOrObservationBase
, DerivedUnit
.class)));
332 } catch (DerivedUnitFacadeNotSupportedException e
) {
333 logger
.warn(tempIndividualsAssociation
.getAssociatedSpecimenOrObservation().getTitleCache() + " : " + e
.getMessage());
339 beanInitializer
.initializeAll(derivedUnitFacadeList
, derivedUnitFacadeInitStrategy
);
341 return derivedUnitFacadeList
;
346 public <T
extends SpecimenOrObservationBase
> List
<T
> listByAssociatedTaxon(Class
<T
> type
, Set
<TaxonRelationshipEdge
> includeRelationships
,
347 Taxon associatedTaxon
, Integer maxDepth
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
349 return pageByAssociatedTaxon(type
, includeRelationships
, associatedTaxon
, maxDepth
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
353 public Collection
<SpecimenNodeWrapper
> listUuidAndTitleCacheByAssociatedTaxon(List
<UUID
> taxonNodeUuids
,
354 Integer limit
, Integer start
) {
355 return dao
.listUuidAndTitleCacheByAssociatedTaxon(taxonNodeUuids
, limit
, start
);
360 public Collection
<FieldUnit
> listFieldUnitsByAssociatedTaxon(Taxon associatedTaxon
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
361 return pageRootUnitsByAssociatedTaxon(FieldUnit
.class, null, associatedTaxon
, null, null, null, null, propertyPaths
).getRecords();
365 public <T
extends SpecimenOrObservationBase
> Collection
<T
> listRootUnitsByAssociatedTaxon(Class
<T
> type
, Taxon associatedTaxon
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
366 return pageRootUnitsByAssociatedTaxon(type
, null, associatedTaxon
, null, null, null, null, propertyPaths
).getRecords();
370 public <T
extends SpecimenOrObservationBase
> Pager
<T
> pageRootUnitsByAssociatedTaxon(Class
<T
> type
, Set
<TaxonRelationshipEdge
> includeRelationships
,
371 Taxon associatedTaxon
, Integer maxDepth
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
372 List
<String
> propertyPaths
) {
374 if (!getSession().contains(associatedTaxon
)) {
375 associatedTaxon
= (Taxon
) taxonService
.load(associatedTaxon
.getUuid());
378 // gather the IDs of all relevant root units
379 Set
<UUID
> rootUnitUuids
= new HashSet
<>();
380 List
<SpecimenOrObservationBase
> records
= listByAssociatedTaxon(null, includeRelationships
, associatedTaxon
, maxDepth
, null, null, orderHints
, propertyPaths
);
381 for (SpecimenOrObservationBase
<?
> specimen
: records
) {
382 for (SpecimenOrObservationBase
<?
> rootUnit
: findRootUnits(specimen
.getUuid(), null)) {
383 if(type
== null || type
.isAssignableFrom(rootUnit
.getClass())) {
384 rootUnitUuids
.add(rootUnit
.getUuid());
388 long totalCount
= rootUnitUuids
.size();
389 //dao.list() does the paging of the field units. Passing the field units directly to the Pager would not work
390 List
<SpecimenOrObservationBase
> rootUnits
= dao
.list(rootUnitUuids
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
391 List
<T
> castedUnits
= new ArrayList
<>(rootUnits
.size());
392 for(SpecimenOrObservationBase
<?
> sob
: rootUnits
) {
393 // this cast should be save since the uuids have been filtered by type above
394 castedUnits
.add((T
)sob
);
396 return new DefaultPagerImpl
<>(pageNumber
, totalCount
, pageSize
, castedUnits
);
400 public FieldUnitDTO
assembleFieldUnitDTO(FieldUnit fieldUnit
) {
402 if (!getSession().contains(fieldUnit
)) {
403 fieldUnit
= (FieldUnit
) load(fieldUnit
.getUuid());
405 // FIXME the filter for SpecimenOrObservationType.PreservedSpecimen has been preserved from the former implementation (see commit 07e3f63c7d and older)
406 // it is questionable if this filter makes sense for all use cases or if it is only a sensible default for the
407 // compressed specimen table in the cdm-dataportal (see #6816, #6870)
408 EnumSet
<SpecimenOrObservationType
> typeIncludeFilter
= EnumSet
.of(SpecimenOrObservationType
.PreservedSpecimen
);
409 FieldUnitDTO fieldUnitDTO
= FieldUnitDTO
.fromEntity(fieldUnit
, null, typeIncludeFilter
);
416 public DerivedUnitDTO
assembleDerivedUnitDTO(DerivedUnit derivedUnit
) {
418 if (!getSession().contains(derivedUnit
)) {
419 derivedUnit
= (DerivedUnit
) load(derivedUnit
.getUuid());
421 DerivedUnitDTO derivedUnitDTO
= DerivedUnitDTO
.fromEntity(derivedUnit
, null, null);
423 // individuals associations
424 Collection
<IndividualsAssociation
> individualsAssociations
= listIndividualsAssociations(derivedUnit
, null, null, null, null);
425 if(individualsAssociations
!= null) {
426 for (IndividualsAssociation individualsAssociation
: individualsAssociations
) {
427 if (individualsAssociation
.getInDescription() != null) {
428 if (individualsAssociation
.getInDescription().isInstanceOf(TaxonDescription
.class)) {
429 TaxonDescription taxonDescription
= HibernateProxyHelper
.deproxy(individualsAssociation
.getInDescription(), TaxonDescription
.class);
430 Taxon taxon
= taxonDescription
.getTaxon();
432 derivedUnitDTO
.addAssociatedTaxon(taxon
);
439 return derivedUnitDTO
;
444 public String
getMostSignificantIdentifier(UUID derivedUnitUuid
) {
445 return dao
.findMostSignificantIdentifier(derivedUnitUuid
);
449 * TODO there is a very similar function in {@link SpecimenOrObservationBaseDTO#assembleDerivative}.
450 * If possible we should avoid using this function here by the method in <code>SpecimenOrObservationBaseDTO</code>.
452 private Set
<DerivedUnitDTO
> getDerivedUnitDTOsFor(SpecimenOrObservationBaseDTO specimenDto
, DerivedUnit specimen
,
453 HashMap
<UUID
, SpecimenOrObservationBaseDTO
> alreadyCollectedSpecimen
) {
455 Set
<DerivedUnitDTO
> derivedUnits
= new HashSet
<>();
456 for (DerivationEvent derivationEvent
: specimen
.getDerivationEvents()) {
457 for (DerivedUnit derivative
: derivationEvent
.getDerivatives()) {
458 if (!alreadyCollectedSpecimen
.containsKey(specimenDto
.getUuid())){
459 DerivedUnitDTO dto
= (DerivedUnitDTO
) SpecimenOrObservationDTOFactory
.fromEntity(derivative
, 0);
460 alreadyCollectedSpecimen
.put(dto
.getUuid(), dto
);
461 dto
.addAllDerivatives(getDerivedUnitDTOsFor(dto
, derivative
, alreadyCollectedSpecimen
));
462 derivedUnits
.add(dto
);
464 if(alreadyCollectedSpecimen
.get(specimenDto
.getUuid()).getDerivatives().isEmpty() && !derivative
.getDerivationEvents().isEmpty()) {
465 // we need to add the missing derivatives!
466 SpecimenOrObservationBaseDTO dto
= alreadyCollectedSpecimen
.get(specimenDto
.getUuid());
467 alreadyCollectedSpecimen
.get(specimenDto
.getUuid()).addAllDerivatives(getDerivedUnitDTOsFor(dto
, derivative
, alreadyCollectedSpecimen
));
475 // private Set<DerivateDTO> getDerivedUnitDTOsFor(DerivateDTO specimenDto, DerivedUnit specimen, HashMap<UUID, DerivateDTO> alreadyCollectedSpecimen) {
476 // Set<DerivateDTO> derivedUnits = new HashSet<>();
478 // for (DerivationEvent derivationEvent : specimen.getDerivationEvents()) {
479 // for (DerivedUnit derivative : derivationEvent.getDerivatives()) {
480 // if (!alreadyCollectedSpecimen.containsKey(specimenDto.getUuid())){
481 // PreservedSpecimenDTO dto;
482 // if (derivative instanceof DnaSample){
483 // dto = DNASampleDTO.newInstance(derivative);
485 // dto = PreservedSpecimenDTO.newInstance(derivative);
487 // alreadyCollectedSpecimen.put(dto.getUuid(), dto);
488 // dto.addAllDerivates(getDerivedUnitDTOsFor(dto, derivative, alreadyCollectedSpecimen));
489 // derivedUnits.add(dto);
493 // return derivedUnits;
497 @SuppressWarnings("unchecked")
499 public <T
extends SpecimenOrObservationBase
> Pager
<T
> pageByAssociatedTaxon(Class
<T
> type
, Set
<TaxonRelationshipEdge
> includedRelationships
,
500 Taxon associatedTaxon
, Integer maxDepth
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
502 Set
<Taxon
> taxa
= new HashSet
<>();
503 Set
<Integer
> occurrenceIds
= new HashSet
<>();
504 List
<T
> occurrences
= new ArrayList
<>();
505 boolean includeUnpublished
= INCLUDE_UNPUBLISHED
;
507 // Integer limit = PagerUtils.limitFor(pageSize);
508 // Integer start = PagerUtils.startFor(pageSize, pageNumber);
510 if (!getSession().contains(associatedTaxon
)) {
511 associatedTaxon
= (Taxon
) taxonService
.load(associatedTaxon
.getUuid());
514 if (includedRelationships
!= null) {
515 taxa
= taxonService
.listRelatedTaxa(associatedTaxon
, includedRelationships
, maxDepth
, includeUnpublished
, null, null, propertyPaths
);
518 taxa
.add(associatedTaxon
);
520 for (Taxon taxon
: taxa
) {
521 List
<T
> perTaxonOccurrences
= dao
.listByAssociatedTaxon(type
, taxon
, null, null, orderHints
, propertyPaths
);
522 for (SpecimenOrObservationBase
<?
> o
: perTaxonOccurrences
) {
523 occurrenceIds
.add(o
.getId());
526 occurrences
= (List
<T
>) dao
.loadList(occurrenceIds
, null, propertyPaths
);
528 return new DefaultPagerImpl
<>(pageNumber
, Long
.valueOf(occurrences
.size()), pageSize
, occurrences
);
533 public <T
extends SpecimenOrObservationBase
> Pager
<T
> pageByAssociatedTaxon(Class
<T
> type
, Set
<TaxonRelationshipEdge
> includeRelationships
,
534 String taxonUUID
, Integer maxDepth
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
536 UUID uuid
= UUID
.fromString(taxonUUID
);
537 Taxon taxon
= (Taxon
) taxonService
.load(uuid
);
538 return pageByAssociatedTaxon(type
, includeRelationships
, taxon
, maxDepth
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
544 public List
<SpecimenOrObservationBaseDTO
> listRootUnitDTOsByAssociatedTaxon(Set
<TaxonRelationshipEdge
> includedRelationships
,
545 UUID associatedTaxonUuid
, List
<String
> propertyPaths
) {
547 Set
<Taxon
> taxa
= new HashSet
<>();
548 Set
<SpecimenOrObservationBaseDTO
> rootUnitDTOs
= new HashSet
<>();
549 boolean includeUnpublished
= INCLUDE_UNPUBLISHED
;
551 Taxon associatedTaxon
= (Taxon
) taxonService
.load(associatedTaxonUuid
);
552 if (includedRelationships
!= null) {
553 taxa
= taxonService
.listRelatedTaxa(associatedTaxon
, includedRelationships
, null, includeUnpublished
, null, null, null);
555 taxa
.add(associatedTaxon
);
557 HashMap
<UUID
, SpecimenOrObservationBaseDTO
> alreadyCollectedUnits
= new HashMap
<>();
558 for (Taxon taxon
: taxa
) {
559 // TODO there might be a good potential to speed up the whole processing by collecting all entities first
560 // and to create the DTOs in a second step
561 Set
<SpecimenOrObservationBase
> perTaxonOccurrences
= dao
.listByAssociatedTaxon(null, taxon
, null, null, null, propertyPaths
)
563 .map(u
-> HibernateProxyHelper
.deproxy(u
, SpecimenOrObservationBase
.class))
564 .collect(Collectors
.toSet());
565 for (SpecimenOrObservationBase
<?
> unit
: perTaxonOccurrences
) {
566 unit
= HibernateProxyHelper
.deproxy(unit
);
567 if (unit
instanceof DerivedUnit
){
568 DerivedUnitDTO derivativeDTO
;
569 if (!alreadyCollectedUnits
.containsKey(unit
.getUuid())){
570 DerivedUnit derivedUnit
= (DerivedUnit
)unit
;
571 boolean isAssociated
= false;
572 for (DeterminationEvent determination
:derivedUnit
.getDeterminations()) {
573 if (determination
.getTaxonName() != null && determination
.getTaxonName().equals(taxon
.getName()) || taxon
.equals(determination
.getTaxon())){
578 for (TaxonDescription desc
: taxon
.getDescriptions()) {
579 for (DescriptionElementBase descElement
: desc
.getElements()) {
580 if (descElement
instanceof IndividualsAssociation
) {
581 if (((IndividualsAssociation
)descElement
).getAssociatedSpecimenOrObservation().getUuid().equals(derivedUnit
.getUuid())) {
589 for (TypeDesignationBase desc
: taxon
.getName().getTypeDesignations()) {
590 if (desc
instanceof SpecimenTypeDesignation
) {
591 if (((SpecimenTypeDesignation
)desc
).getTypeSpecimen().getUuid().equals(derivedUnit
.getUuid())) {
602 derivativeDTO
= (DerivedUnitDTO
) SpecimenOrObservationDTOFactory
.fromEntity(derivedUnit
, null);
603 alreadyCollectedUnits
.put(derivativeDTO
.getUuid(), derivativeDTO
);
604 //derivativeDTO.addAllDerivatives(getDerivedUnitDTOsFor(derivativeDTO, derivedUnit, alreadyCollectedUnits));
606 derivativeDTO
= (DerivedUnitDTO
) alreadyCollectedUnits
.get(unit
.getUuid());
607 rootUnitDTOs
.addAll(findRootUnitDTOs(derivativeDTO
, alreadyCollectedUnits
));
609 // only other option is FieldUnit
610 rootUnitDTOs
.add(FieldUnitDTO
.fromEntity((FieldUnit
)unit
, 0, null));
615 List
<SpecimenOrObservationBaseDTO
> orderdDTOs
= new ArrayList
<>(rootUnitDTOs
);
616 // TODO order dtos by date can only be done by string comparison
617 // the FieldUnitDTO.date needs to be a Partial object for sensible ordering
618 Collections
.sort(orderdDTOs
, new Comparator
<SpecimenOrObservationBaseDTO
>() {
621 public int compare(SpecimenOrObservationBaseDTO o1
, SpecimenOrObservationBaseDTO o2
) {
622 if(o1
instanceof FieldUnitDTO
&& o2
instanceof FieldUnitDTO
) {
623 FieldUnitDTO fu1
= (FieldUnitDTO
)o1
;
624 FieldUnitDTO fu2
= (FieldUnitDTO
)o2
;
625 //TODO if we want null values and values with missing year as smallest we should use
626 //PartialComparator.INSTANCE_NULL_SMALLEST() here
627 return PartialComparator
.INSTANCE().compare(fu1
.getDate(), fu2
.getDate());
629 if(o1
instanceof DerivedUnitDTO
&& o2
instanceof DerivedUnitDTO
) {
630 SpecimenOrObservationBaseDTO du1
= o1
;
631 SpecimenOrObservationBaseDTO du2
= o2
;
632 return StringUtils
.compare(du1
.getLabel(), du2
.getLabel());
634 if(o1
instanceof FieldUnitDTO
&& o2
instanceof DerivedUnitDTO
) {
649 public SpecimenOrObservationBaseDTO
findByAccessionNumber(String accessionNumberString
, List
<OrderHint
> orderHints
) {
650 return findByAccessionNumber(accessionNumberString
, orderHints
);
655 public SpecimenOrObservationBaseDTO
findByGeneticAccessionNumber(String accessionNumberString
, List
<OrderHint
> orderHints
) {
657 DnaSample dnaSample
= dao
.findByGeneticAccessionNumber(accessionNumberString
, null);
658 DerivedUnitDTO dnaSampleDTO
;
659 if (dnaSample
!= null){
660 dnaSampleDTO
= new DNASampleDTO(dnaSample
);
661 Collection
<SpecimenOrObservationBaseDTO
> fieldUnitDTOs
= this.findRootUnitDTOs(dnaSampleDTO
, new HashMap
<>());
662 // FIXME change return type to Collection<FieldUnitDTO>
663 if(fieldUnitDTOs
.isEmpty()) {
666 return fieldUnitDTOs
.iterator().next();
673 public Pager
<SearchResult
<SpecimenOrObservationBase
>> findByFullText(
674 Class
<?
extends SpecimenOrObservationBase
> clazz
, String queryString
, RectangleDTO boundingBox
, List
<Language
> languages
,
675 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
676 List
<String
> propertyPaths
) throws IOException
, LuceneParseException
{
678 LuceneSearch luceneSearch
= prepareByFullTextSearch(clazz
, queryString
, boundingBox
, languages
, highlightFragments
);
680 // --- execute search
681 TopGroups
<BytesRef
> topDocsResultSet
;
683 topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
684 } catch (ParseException e
) {
685 LuceneParseException parseException
= new LuceneParseException(e
.getMessage());
686 parseException
.setStackTrace(e
.getStackTrace());
687 throw parseException
;
690 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<>();
691 idFieldMap
.put(CdmBaseType
.SPECIMEN_OR_OBSERVATIONBASE
, "id");
693 // --- initialize taxa, highlight matches ....
694 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
695 @SuppressWarnings("rawtypes")
696 List
<SearchResult
<SpecimenOrObservationBase
>> searchResults
= searchResultBuilder
.createResultSet(
697 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
699 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.totalGroupCount
: 0;
701 return new DefaultPagerImpl
<>(pageNumber
, Long
.valueOf(totalHits
), pageSize
, searchResults
);
704 private LuceneSearch
prepareByFullTextSearch(Class
<?
extends SpecimenOrObservationBase
> clazz
, String queryString
, RectangleDTO bbox
,
705 List
<Language
> languages
, boolean highlightFragments
) {
707 Builder finalQueryBuilder
= new Builder();
708 Builder textQueryBuilder
= new Builder();
710 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, FieldUnit
.class);
711 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(FieldUnit
.class);
714 luceneSearch
.setCdmTypRestriction(clazz
);
715 if (queryString
!= null) {
716 textQueryBuilder
.add(queryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
717 finalQueryBuilder
.add(textQueryBuilder
.build(), Occur
.MUST
);
722 finalQueryBuilder
.add(QueryFactory
.buildSpatialQueryByRange(bbox
, "gatheringEvent.exactLocation.point"), Occur
.MUST
);
725 luceneSearch
.setQuery(finalQueryBuilder
.build());
728 SortField
[] sortFields
= new SortField
[] { SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.Type
.STRING
, false) };
729 luceneSearch
.setSortFields(sortFields
);
731 if (highlightFragments
) {
732 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
739 @Transactional(readOnly
=true)
740 public Collection
<FieldUnit
> findFieldUnits(UUID derivedUnitUuid
, List
<String
> propertyPaths
) {
741 //It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
742 //from which this DerivedUnit was derived until all FieldUnits are found.
744 // FIXME: use HQL queries to avoid entity instantiation and to increase performance
746 SpecimenOrObservationBase
<?
> specimen
= load(derivedUnitUuid
);
747 Collection
<FieldUnit
> fieldUnits
= new ArrayList
<>();
748 if (specimen
== null){
751 if (specimen
.isInstanceOf(FieldUnit
.class)) {
752 fieldUnits
.add(HibernateProxyHelper
.deproxy(specimen
, FieldUnit
.class));
754 else if(specimen
.isInstanceOf(DerivedUnit
.class)){
755 fieldUnits
.addAll(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class).collectRootUnits(FieldUnit
.class));
758 fieldUnits
= beanInitializer
.initializeAll(fieldUnits
, propertyPaths
);
763 @Transactional(readOnly
=true)
764 public Collection
<SpecimenOrObservationBase
> findRootUnits(UUID derivedUnitUuid
, List
<String
> propertyPaths
) {
766 // FIXME: use HQL queries to avoid entity instantiation and to increase performance
768 SpecimenOrObservationBase
<?
> specimen
= load(derivedUnitUuid
);
769 Collection
<SpecimenOrObservationBase
> rootUnits
= new ArrayList
<>();
770 if (specimen
== null){
773 if (specimen
.isInstanceOf(FieldUnit
.class)) {
774 rootUnits
.add(HibernateProxyHelper
.deproxy(specimen
, FieldUnit
.class));
776 else if(specimen
.isInstanceOf(DerivedUnit
.class)){
777 rootUnits
.addAll(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class).collectRootUnits(SpecimenOrObservationBase
.class));
780 rootUnits
= beanInitializer
.initializeAll(rootUnits
, propertyPaths
);
785 public Collection
<SpecimenOrObservationBaseDTO
> findRootUnitDTOs(UUID unitUUID
) {
787 SpecimenOrObservationBase
<?
> entity
= load(unitUUID
);
788 SpecimenOrObservationBaseDTO derivedUnitDTO
= SpecimenOrObservationDTOFactory
.fromEntity(entity
);
789 Collection
<SpecimenOrObservationBaseDTO
> rootUnitDTOs
= new ArrayList
<>();
790 if(derivedUnitDTO
!= null) {
791 if(derivedUnitDTO
instanceof FieldUnitDTO
) {
792 rootUnitDTOs
.add(derivedUnitDTO
);
794 Map
<UUID
, SpecimenOrObservationBaseDTO
> alreadyCollectedSpecimen
= new HashMap
<>();
795 rootUnitDTOs
= findRootUnitDTOs((DerivedUnitDTO
)derivedUnitDTO
, alreadyCollectedSpecimen
);
804 * Recursively searches all {@link DerivationEvent}s to find all "originals" ({@link SpecimenOrObservationBase})
805 * from which this DerivedUnit was derived until all FieldUnits are found.
807 * <b>NOTE:</b> The recursive search still is a bit incomplete and may miss originals in the rare case where a
808 * derivative has more than one original. (see https://dev.e-taxonomy.eu/redmine/issues/9253)
810 * @param derivedUnitDTO
811 * The DerivedUnitDTO to start the search from.
812 * @param alreadyCollectedSpecimen
813 * A map to hold all originals that have been sees during the recursive walk.
815 * The collection of all Field Units that are accessible from the derivative from where the search was started.
817 public Collection
<SpecimenOrObservationBaseDTO
> findRootUnitDTOs(DerivedUnitDTO derivedUnitDTO
,
818 Map
<UUID
, SpecimenOrObservationBaseDTO
> alreadyCollectedSpecimen
) {
820 HashMap
<UUID
, SpecimenOrObservationBaseDTO
> rootUnitDTOs
= new HashMap
<>();
821 _findRootUnitDTOs(derivedUnitDTO
, rootUnitDTOs
, alreadyCollectedSpecimen
);
822 return rootUnitDTOs
.values();
827 * Method for recursive calls, must only be used by {@link #findRootUnitDTOs(DerivedUnitDTO, HashMap)}
829 * It will search recursively over all {@link DerivationEvent}s and get the "originals" ({@link SpecimenOrObservationBase})
830 * from which this DerivedUnit was derived until all FieldUnits are found.
832 private void _findRootUnitDTOs(DerivedUnitDTO derivedUnitDTO
, Map
<UUID
, SpecimenOrObservationBaseDTO
> rootUnitDTOs
,
833 Map
<UUID
, SpecimenOrObservationBaseDTO
> alreadyCollectedSpecimen
) {
835 List
<String
> propertyPaths
= new ArrayList
<>();
837 // add the supplied DTO the the alreadyCollectedSpecimen if not yet there
838 if(!alreadyCollectedSpecimen
.containsKey(derivedUnitDTO
.getUuid())) {
839 alreadyCollectedSpecimen
.put(derivedUnitDTO
.getUuid(), derivedUnitDTO
);
842 List
<SpecimenOrObservationBase
> originals
= dao
.findOriginalsForDerivedUnit(derivedUnitDTO
.getUuid(), propertyPaths
);
843 if (originals
.size() > 0){
844 if (originals
.size() > 1){
845 logger
.warn("The derived unit with uuid " + derivedUnitDTO
.getUuid() + "has more than one orginal");
847 // FIXME allow handling multiple originals
848 SpecimenOrObservationBase
<?
> original
= originals
.get(0);
849 original
= HibernateProxyHelper
.deproxy(original
);
851 if (alreadyCollectedSpecimen
.containsKey(original
.getUuid())){
852 alreadyCollectedSpecimen
.get(original
.getUuid()).addDerivative(derivedUnitDTO
);
853 // if ( alreadyCollectedSpecimen.get(specimen.getUuid()) instanceof FieldUnitDTO){
854 // ((FieldUnitDTO)alreadyCollectedSpecimen.get(specimen.getUuid())).getTaxonRelatedDerivedUnits().add(derivedUnitDTO.getUuid());
857 if(!rootUnitDTOs
.containsKey(original
.getUuid())){
858 // the direct derivatives of the field unit are added in the factory method, so it is guaranteed that
859 // the derivedUnitDTO is already contained.
861 // Don't assemble derivatives for originals since we have them collected already
862 // when ascending to the originals, we only want to collect those derivatives which are on the path up to the root
863 final Integer maxDepth
= 0;
864 SpecimenOrObservationBaseDTO originalDTO
= SpecimenOrObservationDTOFactory
.fromEntity(original
, maxDepth
);
865 originalDTO
.addDerivative(derivedUnitDTO
);
866 alreadyCollectedSpecimen
.put(originalDTO
.getUuid(), originalDTO
);
867 if (original
instanceof FieldUnit
){
868 rootUnitDTOs
.put(originalDTO
.getUuid(), originalDTO
);
870 _findRootUnitDTOs((DerivedUnitDTO
) originalDTO
, rootUnitDTOs
, alreadyCollectedSpecimen
);
873 SpecimenOrObservationBaseDTO previouslyFoundRootUnit
= rootUnitDTOs
.get(original
.getUuid());
874 if(!previouslyFoundRootUnit
.getDerivatives().stream().anyMatch(uDTO
-> uDTO
.getUuid().equals(derivedUnitDTO
.getUuid()))) {
875 previouslyFoundRootUnit
.addDerivative(derivedUnitDTO
);
880 rootUnitDTOs
.put(derivedUnitDTO
.getUuid(), derivedUnitDTO
);
886 @Transactional(readOnly
=true)
887 public FieldUnitDTO
loadFieldUnitDTO(UUID derivedUnitUuid
) {
889 FieldUnitDTO fieldUnitDTO
= null;
890 DerivedUnitDTO derivedUnitDTO
= null;
892 Map
<UUID
, SpecimenOrObservationBaseDTO
> cycleDetectionMap
= new HashMap
<>();
893 SpecimenOrObservationBase
<?
> derivative
= dao
.load(derivedUnitUuid
);
894 if(derivative
!= null){
895 if (derivative
instanceof FieldUnit
){
896 fieldUnitDTO
= FieldUnitDTO
.fromEntity((FieldUnit
)derivative
);
899 // must be a DerivedUnit otherwise
900 derivedUnitDTO
= DerivedUnitDTO
.fromEntity((DerivedUnit
)derivative
);
903 Set
<SpecimenOrObservationBaseDTO
> originals
= originalDTOs(derivedUnitDTO
.getUuid());
905 if(originals
.isEmpty()){
908 if (originals
.size() > 1){
909 logger
.debug("The derived unit with uuid " + derivedUnitUuid
+ "has more than one orginal, ignoring all but the first one.");
912 SpecimenOrObservationBaseDTO originalDTO
= originals
.iterator().next();
914 // cycle detection and handling
915 if(cycleDetectionMap
.containsKey(originalDTO
.getUuid())){
918 throw new Exception();
919 } catch(Exception e
){
920 logger
.error("Cycle in derivate graph detected at DerivedUnit with uuid=" + originalDTO
.getUuid() , e
);
922 // to solve the situation for the output we remove the derivate from the more distant graph node
923 // by removing it from the derivatives of its original
924 // but let the derivate to be added to the original which is closer to the FieldUnit (below at originalDTO.addDerivate(derivedUnitDTO);)
925 for(SpecimenOrObservationBaseDTO seenOriginal
: cycleDetectionMap
.values()){
926 for(SpecimenOrObservationBaseDTO derivateDTO
: seenOriginal
.getDerivatives()){
927 if(derivateDTO
.equals(originalDTO
)){
928 seenOriginal
.getDerivatives().remove(originalDTO
);
933 cycleDetectionMap
.put(originalDTO
.getUuid(), originalDTO
);
936 if (originalDTO
instanceof FieldUnitDTO
){
937 fieldUnitDTO
= (FieldUnitDTO
)originalDTO
;
940 // So this must be a DerivedUnitDTO
941 if (derivedUnitDTO
== null){
942 derivedUnitDTO
= (DerivedUnitDTO
)originalDTO
;
944 derivedUnitDTO
= (DerivedUnitDTO
)originalDTO
;
958 private Set
<SpecimenOrObservationBaseDTO
> originalDTOs(UUID derivativeUuid
) {
960 Set
<SpecimenOrObservationBaseDTO
> dtos
= new HashSet
<>();
961 List
<SpecimenOrObservationBase
> specimens
= dao
.findOriginalsForDerivedUnit(derivativeUuid
, null);
962 for(SpecimenOrObservationBase sob
: specimens
){
963 if(sob
instanceof FieldUnit
) {
964 dtos
.add(FieldUnitDTO
.fromEntity((FieldUnit
)sob
));
966 dtos
.add(DerivedUnitDTO
.fromEntity((DerivedUnit
)sob
));
974 @Transactional(readOnly
= false)
975 public UpdateResult
moveSequence(DnaSample from
, DnaSample to
, Sequence sequence
) {
976 return moveSequence(from
.getUuid(), to
.getUuid(), sequence
.getUuid());
980 @Transactional(readOnly
= false)
981 public UpdateResult
moveSequence(UUID fromUuid
, UUID toUuid
, UUID sequenceUuid
) {
982 // reload specimens to avoid session conflicts
983 DnaSample from
= (DnaSample
) load(fromUuid
);
984 DnaSample to
= (DnaSample
) load(toUuid
);
985 Sequence sequence
= sequenceService
.load(sequenceUuid
);
987 if (from
== null || to
== null || sequence
== null) {
988 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" +
989 "Operation was move "+sequence
+ " from "+from
+" to "+to
);
991 UpdateResult result
= new UpdateResult();
992 from
.removeSequence(sequence
);
994 to
.addSequence(sequence
);
996 result
.setStatus(Status
.OK
);
997 result
.addUpdatedObject(from
);
998 result
.addUpdatedObject(to
);
1003 @Transactional(readOnly
= false)
1004 public boolean moveDerivate(SpecimenOrObservationBase
<?
> from
, SpecimenOrObservationBase
<?
> to
, DerivedUnit derivate
) {
1005 return moveDerivate(from
!=null?from
.getUuid():null, to
.getUuid(), derivate
.getUuid()).isOk();
1009 @Transactional(readOnly
= false)
1010 public UpdateResult
moveDerivate(UUID specimenFromUuid
, UUID specimenToUuid
, UUID derivateUuid
) {
1011 // reload specimens to avoid session conflicts
1012 SpecimenOrObservationBase
<?
> from
= null;
1013 if(specimenFromUuid
!=null){
1014 from
= load(specimenFromUuid
);
1016 SpecimenOrObservationBase
<?
> to
= load(specimenToUuid
);
1017 DerivedUnit derivate
= (DerivedUnit
) load(derivateUuid
);
1019 if ((specimenFromUuid
!=null && from
== null) || to
== null || derivate
== null) {
1020 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" +
1021 "Operation was move "+derivate
+ " from "+from
+" to "+to
);
1023 UpdateResult result
= new UpdateResult();
1024 SpecimenOrObservationType derivateType
= derivate
.getRecordBasis();
1025 SpecimenOrObservationType toType
= to
.getRecordBasis();
1026 // check if type is a sub derivate type
1027 if(toType
==SpecimenOrObservationType
.FieldUnit
//moving to FieldUnit always works
1028 || derivateType
==SpecimenOrObservationType
.Media
//moving media always works
1029 || (derivateType
.isKindOf(toType
) && toType
!=derivateType
)){ //moving only to parent derivate type
1031 // remove derivation event from parent specimen of dragged object
1032 DerivationEvent eventToRemove
= null;
1033 for (DerivationEvent event
: from
.getDerivationEvents()) {
1034 if (event
.getDerivatives().contains(derivate
)) {
1035 eventToRemove
= event
;
1039 from
.removeDerivationEvent(eventToRemove
);
1040 if(eventToRemove
!=null){
1041 // add new derivation event to target and copy the event parameters of the old one
1042 DerivationEvent derivedFromNewOriginalEvent
= DerivationEvent
.NewSimpleInstance(to
, derivate
, null);
1043 derivedFromNewOriginalEvent
.setActor(eventToRemove
.getActor());
1044 derivedFromNewOriginalEvent
.setDescription(eventToRemove
.getDescription());
1045 derivedFromNewOriginalEvent
.setInstitution(eventToRemove
.getInstitution());
1046 derivedFromNewOriginalEvent
.setTimeperiod(eventToRemove
.getTimeperiod());
1047 derivedFromNewOriginalEvent
.setType(eventToRemove
.getType());
1048 to
.addDerivationEvent(derivedFromNewOriginalEvent
);
1049 derivate
.setDerivedFrom(derivedFromNewOriginalEvent
);
1053 //derivative had no parent before so we use empty derivation event
1054 DerivationEvent derivedFromNewOriginalEvent
= DerivationEvent
.NewSimpleInstance(to
, derivate
, null);
1055 to
.addDerivationEvent(derivedFromNewOriginalEvent
);
1056 derivate
.setDerivedFrom(derivedFromNewOriginalEvent
);
1063 result
.setStatus(Status
.OK
);
1064 result
.addUpdatedObject(from
);
1065 result
.addUpdatedObject(to
);
1067 result
.setStatus(Status
.ERROR
);
1073 public DeleteResult
isDeletable(UUID specimenUuid
, DeleteConfiguratorBase config
) {
1074 DeleteResult deleteResult
= new DeleteResult();
1075 SpecimenOrObservationBase
<?
> specimen
= this.load(specimenUuid
);
1076 SpecimenDeleteConfigurator specimenDeleteConfigurator
= (SpecimenDeleteConfigurator
) config
;
1078 // check elements found by super method
1079 Set
<CdmBase
> relatedObjects
= super.isDeletable(specimenUuid
, config
).getRelatedObjects();
1080 for (CdmBase cdmBase
: relatedObjects
) {
1081 // check for type designation
1082 if (cdmBase
.isInstanceOf(SpecimenTypeDesignation
.class) && !specimenDeleteConfigurator
.isDeleteFromTypeDesignation()) {
1083 deleteResult
.setAbort();
1084 deleteResult
.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is a type specimen."));
1085 deleteResult
.addRelatedObject(cdmBase
);
1088 // check for IndividualsAssociations
1089 else if (cdmBase
.isInstanceOf(IndividualsAssociation
.class) && !specimenDeleteConfigurator
.isDeleteFromIndividualsAssociation()) {
1090 deleteResult
.setAbort();
1091 deleteResult
.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still associated via IndividualsAssociations"));
1092 deleteResult
.addRelatedObject(cdmBase
);
1095 // check for taxon description
1096 else if(cdmBase
.isInstanceOf(TaxonDescription
.class)
1097 && HibernateProxyHelper
.deproxy(cdmBase
, TaxonDescription
.class).getDescribedSpecimenOrObservation().equals(specimen
)
1098 && !specimenDeleteConfigurator
.isDeleteFromDescription()){
1099 deleteResult
.setAbort();
1100 deleteResult
.addException(new ReferencedObjectUndeletableException("Specimen or obeservation is still used as \"Described Specimen\" in a taxon description."));
1101 deleteResult
.addRelatedObject(cdmBase
);
1104 // check for children and parents (derivation events)
1105 else if (cdmBase
.isInstanceOf(DerivationEvent
.class)) {
1106 DerivationEvent derivationEvent
= HibernateProxyHelper
.deproxy(cdmBase
, DerivationEvent
.class);
1107 // check if derivation event is empty
1108 if (!derivationEvent
.getDerivatives().isEmpty() && derivationEvent
.getOriginals().contains(specimen
)) {
1109 // if derivationEvent is the childEvent and contains derivations
1110 // if (derivationEvent.getDerivatives().contains(specimen)) {
1111 // //if it is the parent event the specimen is still deletable
1114 if(!specimenDeleteConfigurator
.isDeleteChildren()){
1115 //if children should not be deleted then it is undeletable
1116 deleteResult
.setAbort();
1117 deleteResult
.addException(new ReferencedObjectUndeletableException("Specimen or obeservation still has child derivatives."));
1118 deleteResult
.addRelatedObject(cdmBase
);
1122 // check all children if they can be deleted
1123 Set
<DerivedUnit
> derivatives
= derivationEvent
.getDerivatives();
1124 DeleteResult childResult
= new DeleteResult();
1125 for (DerivedUnit derivedUnit
: derivatives
) {
1126 childResult
.includeResult(isDeletable(derivedUnit
.getUuid(), specimenDeleteConfigurator
));
1128 if (!childResult
.isOk()) {
1129 deleteResult
.setAbort();
1130 deleteResult
.includeResult(childResult
);
1131 deleteResult
.addRelatedObject(cdmBase
);
1137 // check for amplification
1138 else if (cdmBase
.isInstanceOf(AmplificationResult
.class)
1139 && !specimenDeleteConfigurator
.isDeleteMolecularData()
1140 && !specimenDeleteConfigurator
.isDeleteChildren()) {
1141 deleteResult
.setAbort();
1142 deleteResult
.addException(new ReferencedObjectUndeletableException("DnaSample is used in amplification results."));
1143 deleteResult
.addRelatedObject(cdmBase
);
1146 // check for sequence
1147 else if (cdmBase
.isInstanceOf(Sequence
.class)
1148 && !specimenDeleteConfigurator
.isDeleteMolecularData()
1149 && !specimenDeleteConfigurator
.isDeleteChildren()) {
1150 deleteResult
.setAbort();
1151 deleteResult
.addException(new ReferencedObjectUndeletableException("DnaSample is used in sequences."));
1152 deleteResult
.addRelatedObject(cdmBase
);
1156 if (deleteResult
.isOk()) {
1157 //add all related object if deletion is OK so they can be handled by the delete() method
1158 deleteResult
.addRelatedObjects(relatedObjects
);
1160 return deleteResult
;
1163 @Transactional(readOnly
= false)
1165 public DeleteResult
delete(UUID specimenUuid
, SpecimenDeleteConfigurator config
) {
1166 return delete(load(specimenUuid
), config
);
1169 @Transactional(readOnly
= false)
1171 public DeleteResult
delete(SpecimenOrObservationBase
<?
> specimen
, SpecimenDeleteConfigurator config
) {
1172 specimen
= HibernateProxyHelper
.deproxy(specimen
, SpecimenOrObservationBase
.class);
1174 DeleteResult deleteResult
= isDeletable(specimen
.getUuid(), config
);
1175 if (!deleteResult
.isOk()) {
1176 return deleteResult
;
1179 if (config
.isDeleteChildren()) {
1180 Set
<DerivationEvent
> derivationEvents
= specimen
.getDerivationEvents();
1181 //clone to avoid concurrent modification
1182 //can happen if the child is deleted and deleted its own derivedFrom event
1183 Set
<DerivationEvent
> derivationEventsClone
= new HashSet
<>(derivationEvents
);
1184 for (DerivationEvent derivationEvent
: derivationEventsClone
) {
1185 Set
<DerivedUnit
> derivatives
= derivationEvent
.getDerivatives();
1186 Iterator
<DerivedUnit
> it
= derivatives
.iterator();
1187 Set
<DerivedUnit
> derivativesToDelete
= new HashSet
<>();
1188 while (it
.hasNext()) {
1189 DerivedUnit unit
= it
.next();
1190 derivativesToDelete
.add(unit
);
1192 for (DerivedUnit unit
: derivativesToDelete
){
1193 DeleteResult derivedDeleteResult
= delete(unit
, config
);
1194 deleteResult
.includeResult(derivedDeleteResult
);
1199 // check related objects
1200 Set
<CdmBase
> relatedObjects
= deleteResult
.getRelatedObjects();
1202 for (CdmBase relatedObject
: relatedObjects
) {
1203 // check for TypeDesignations
1204 if (relatedObject
.isInstanceOf(SpecimenTypeDesignation
.class)) {
1205 SpecimenTypeDesignation designation
= HibernateProxyHelper
.deproxy(relatedObject
, SpecimenTypeDesignation
.class);
1206 designation
.setTypeSpecimen(null);
1207 List
<TaxonName
> typifiedNames
= new ArrayList
<>();
1208 typifiedNames
.addAll(designation
.getTypifiedNames());
1209 for (TaxonName taxonName
: typifiedNames
) {
1210 taxonName
.removeTypeDesignation(designation
);
1213 // delete IndividualsAssociation
1214 if (relatedObject
.isInstanceOf(IndividualsAssociation
.class)) {
1215 IndividualsAssociation association
= HibernateProxyHelper
.deproxy(relatedObject
, IndividualsAssociation
.class);
1216 association
.setAssociatedSpecimenOrObservation(null);
1217 association
.getInDescription().removeElement(association
);
1219 // check for "described specimen" (deprecated)
1220 if (relatedObject
.isInstanceOf(TaxonDescription
.class)) {
1221 TaxonDescription description
= HibernateProxyHelper
.deproxy(relatedObject
, TaxonDescription
.class);
1222 description
.setDescribedSpecimenOrObservation(null);
1224 // check for specimen description
1225 if (relatedObject
.isInstanceOf(SpecimenDescription
.class)) {
1226 SpecimenDescription specimenDescription
= HibernateProxyHelper
.deproxy(relatedObject
, SpecimenDescription
.class);
1227 specimenDescription
.setDescribedSpecimenOrObservation(null);
1228 // check if description is a description of the given specimen
1229 if (specimen
.getDescriptions().contains(specimenDescription
)) {
1230 specimen
.removeDescription(specimenDescription
);
1232 DeleteResult descriptionDelete
= descriptionService
.isDeletable(specimenDescription
.getUuid(), null);
1233 if (descriptionDelete
.isOk()){
1234 deleteResult
.includeResult(descriptionService
.delete(specimenDescription
));
1237 // check for amplification
1238 if (relatedObject
.isInstanceOf(AmplificationResult
.class)) {
1239 AmplificationResult amplificationResult
= HibernateProxyHelper
.deproxy(relatedObject
, AmplificationResult
.class);
1240 amplificationResult
.getDnaSample().removeAmplificationResult(amplificationResult
);
1242 // check for sequence
1243 if (relatedObject
.isInstanceOf(Sequence
.class)) {
1244 Sequence sequence
= HibernateProxyHelper
.deproxy(relatedObject
, Sequence
.class);
1245 sequence
.getDnaSample().removeSequence(sequence
);
1247 // check for children and parents (derivation events)
1248 if (relatedObject
.isInstanceOf(DerivationEvent
.class)) {
1249 DerivationEvent derivationEvent
= HibernateProxyHelper
.deproxy(relatedObject
, DerivationEvent
.class);
1250 // parent derivation event (derivedFrom)
1251 if (derivationEvent
.getDerivatives().contains(specimen
) && specimen
.isInstanceOf(DerivedUnit
.class)) {
1252 derivationEvent
.removeDerivative(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
1253 if (derivationEvent
.getDerivatives().isEmpty()) {
1254 Set
<SpecimenOrObservationBase
> originals
= new HashSet
<>();
1255 originals
.addAll(derivationEvent
.getOriginals());
1256 for (SpecimenOrObservationBase specimenOrObservationBase
: originals
) {
1257 specimenOrObservationBase
.removeDerivationEvent(derivationEvent
);
1258 deleteResult
.addUpdatedObject(specimenOrObservationBase
);
1260 // if derivationEvent has no derivates anymore, delete it
1261 deleteResult
.includeResult(eventService
.delete(derivationEvent
));
1265 //child derivation events should not occur since we delete the hierarchy from bottom to top
1269 if (specimen
instanceof FieldUnit
){
1270 FieldUnit fieldUnit
= HibernateProxyHelper
.deproxy(specimen
, FieldUnit
.class);
1271 GatheringEvent event
= fieldUnit
.getGatheringEvent();
1272 fieldUnit
.setGatheringEvent(null);
1274 DeleteResult result
= eventService
.isDeletable(event
.getUuid(), null);
1276 deleteResult
.includeResult( eventService
.delete(event
));
1281 deleteResult
.includeResult(delete(specimen
));
1283 return deleteResult
;
1287 public Collection
<IndividualsAssociation
> listIndividualsAssociations(SpecimenOrObservationBase
<?
> specimen
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1288 return dao
.listIndividualsAssociations(specimen
, limit
, start
, orderHints
, propertyPaths
);
1295 public Collection
<TaxonBase
<?
>> listAssociatedTaxa(SpecimenOrObservationBase
<?
> specimen
, Integer limit
,
1296 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1297 return listAssociatedTaxa(specimen
, INCLUDE_UNPUBLISHED
, limit
, start
, orderHints
, propertyPaths
);
1300 public Collection
<TaxonBase
<?
>> listAssociatedTaxa(SpecimenOrObservationBase
<?
> specimen
, boolean includeUnpublished
,
1301 Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1302 Collection
<TaxonBase
<?
>> associatedTaxa
= new HashSet
<>();
1304 //individuals associations
1305 associatedTaxa
.addAll(listIndividualsAssociationTaxa(specimen
, includeUnpublished
, limit
, start
, orderHints
, propertyPaths
));
1307 if(specimen
.isInstanceOf(DerivedUnit
.class)){
1308 associatedTaxa
.addAll(listTypeDesignationTaxa(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class),
1309 includeUnpublished
, limit
, start
, orderHints
, propertyPaths
));
1312 associatedTaxa
.addAll(listDeterminedTaxa(specimen
, includeUnpublished
, limit
, start
, orderHints
, propertyPaths
));
1314 return associatedTaxa
;
1323 public Collection
<TaxonBase
<?
>> listDeterminedTaxa(SpecimenOrObservationBase
<?
> specimen
, Integer limit
,
1324 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1325 return listDeterminedTaxa(specimen
, INCLUDE_UNPUBLISHED
, limit
, start
, orderHints
, propertyPaths
);
1328 public Collection
<TaxonBase
<?
>> listDeterminedTaxa(SpecimenOrObservationBase
<?
> specimen
, boolean includeUnpublished
, Integer limit
, Integer start
,
1329 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1330 Collection
<TaxonBase
<?
>> associatedTaxa
= new HashSet
<>();
1331 for (DeterminationEvent determinationEvent
: listDeterminationEvents(specimen
, limit
, start
, orderHints
, propertyPaths
)) {
1332 if(determinationEvent
.getIdentifiedUnit().equals(specimen
)){
1333 if(determinationEvent
.getTaxon()!=null){
1334 associatedTaxa
.add(taxonService
.load(determinationEvent
.getTaxon().getUuid(), includeUnpublished
, propertyPaths
));
1336 if(determinationEvent
.getTaxonName()!=null){
1337 Collection
<TaxonBase
> taxonBases
= determinationEvent
.getTaxonName().getTaxonBases();
1338 for (TaxonBase taxonBase
: taxonBases
) {
1339 associatedTaxa
.add(taxonService
.load(taxonBase
.getUuid(), includeUnpublished
, propertyPaths
));
1344 return associatedTaxa
;
1351 public Collection
<TaxonBase
<?
>> listTypeDesignationTaxa(DerivedUnit specimen
, Integer limit
, Integer start
,
1352 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1353 return listTypeDesignationTaxa(specimen
, INCLUDE_UNPUBLISHED
, limit
, start
, orderHints
, propertyPaths
);
1356 public Collection
<TaxonBase
<?
>> listTypeDesignationTaxa(DerivedUnit specimen
, boolean includeUnpublished
,
1357 Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1358 Collection
<TaxonBase
<?
>> associatedTaxa
= new HashSet
<>();
1359 for (SpecimenTypeDesignation typeDesignation
: listTypeDesignations(specimen
, limit
, start
, orderHints
, propertyPaths
)) {
1360 if(typeDesignation
.getTypeSpecimen().equals(specimen
)){
1361 Set
<TaxonName
> typifiedNames
= typeDesignation
.getTypifiedNames();
1362 for (TaxonName taxonName
: typifiedNames
) {
1363 Set
<Taxon
> taxa
= taxonName
.getTaxa();
1364 for (Taxon taxon
: taxa
) {
1365 associatedTaxa
.add(taxonService
.load(taxon
.getUuid(), includeUnpublished
, propertyPaths
));
1370 return associatedTaxa
;
1377 public Collection
<TaxonBase
<?
>> listIndividualsAssociationTaxa(SpecimenOrObservationBase
<?
> specimen
, Integer limit
,
1378 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1379 return listIndividualsAssociationTaxa(specimen
, INCLUDE_UNPUBLISHED
, limit
, start
, orderHints
, propertyPaths
);
1383 public Collection
<TaxonBase
<?
>> listIndividualsAssociationTaxa(SpecimenOrObservationBase
<?
> specimen
, boolean includeUnpublished
,
1384 Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1385 Collection
<TaxonBase
<?
>> associatedTaxa
= new HashSet
<>();
1386 for (IndividualsAssociation individualsAssociation
: listIndividualsAssociations(specimen
, null, null, null, null)) {
1387 if(individualsAssociation
.getInDescription().isInstanceOf(TaxonDescription
.class)){
1388 TaxonDescription taxonDescription
= HibernateProxyHelper
.deproxy(individualsAssociation
.getInDescription(), TaxonDescription
.class);
1389 if(taxonDescription
.getTaxon()!=null){
1390 associatedTaxa
.add(taxonService
.load(taxonDescription
.getTaxon().getUuid(), includeUnpublished
, propertyPaths
));
1394 return associatedTaxa
;
1398 public Collection
<DeterminationEvent
> listDeterminationEvents(SpecimenOrObservationBase
<?
> specimen
,
1399 Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1400 return dao
.listDeterminationEvents(specimen
, limit
, start
, orderHints
, propertyPaths
);
1404 public Map
<DerivedUnit
, Collection
<SpecimenTypeDesignation
>> listTypeDesignations(
1405 Collection
<DerivedUnit
> specimens
, Integer limit
, Integer start
,
1406 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1407 Map
<DerivedUnit
, Collection
<SpecimenTypeDesignation
>> typeDesignationMap
= new HashMap
<>();
1408 for (DerivedUnit specimen
: specimens
) {
1409 Collection
<SpecimenTypeDesignation
> typeDesignations
= listTypeDesignations(specimen
, limit
, start
, orderHints
, propertyPaths
);
1410 typeDesignationMap
.put(specimen
, typeDesignations
);
1412 return typeDesignationMap
;
1416 public Collection
<SpecimenTypeDesignation
> listTypeDesignations(DerivedUnit specimen
,
1417 Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1418 return dao
.listTypeDesignations(specimen
, limit
, start
, orderHints
, propertyPaths
);
1422 public Collection
<DescriptionBase
<?
>> listDescriptionsWithDescriptionSpecimen(
1423 SpecimenOrObservationBase
<?
> specimen
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
,
1424 List
<String
> propertyPaths
) {
1425 return dao
.listDescriptionsWithDescriptionSpecimen(specimen
, limit
, start
, orderHints
, propertyPaths
);
1429 public Collection
<DescriptionElementBase
> getCharacterDataForSpecimen(UUID specimenUuid
) {
1430 SpecimenOrObservationBase
<?
> specimen
= load(specimenUuid
);
1431 if (specimen
!= null) {
1432 return specimen
.characterData();
1435 throw new DataRetrievalFailureException("Specimen with the given uuid not found in the data base");
1440 public long countByTitle(IIdentifiableEntityServiceConfigurator
<SpecimenOrObservationBase
> config
){
1441 if (config
instanceof FindOccurrencesConfigurator
) {
1442 FindOccurrencesConfigurator occurrenceConfig
= (FindOccurrencesConfigurator
) config
;
1444 if(occurrenceConfig
.getAssociatedTaxonUuid()!=null){
1445 TaxonBase
<?
> taxonBase
= taxonService
.load(occurrenceConfig
.getAssociatedTaxonUuid());
1446 if(taxonBase
!= null && taxonBase
.isInstanceOf(Taxon
.class)){
1447 taxon
= HibernateProxyHelper
.deproxy(taxonBase
, Taxon
.class);
1450 TaxonName taxonName
= null;
1451 if(occurrenceConfig
.getAssociatedTaxonNameUuid()!=null){
1452 taxonName
= nameService
.load(occurrenceConfig
.getAssociatedTaxonNameUuid());
1454 /*TODO: #6484 Neither isRetrieveIndirectlyAssociatedSpecimens() nor the AssignmentStatus
1455 * is currently reflected in the HQL query. So using these in the count method will
1456 * significantly slow down this method as we have to retrieve the entities instead of
1459 if(occurrenceConfig
.isRetrieveIndirectlyAssociatedSpecimens() || !occurrenceConfig
.getAssignmentStatus().equals(AssignmentStatus
.ALL_SPECIMENS
)){
1460 List
<SpecimenOrObservationBase
> occurrences
= new ArrayList
<>();
1461 List
<SpecimenOrObservationBase
> sobs
= dao
.findOccurrences(occurrenceConfig
.getClazz(),
1462 occurrenceConfig
.getTitleSearchStringSqlized(), occurrenceConfig
.getSignificantIdentifier(),
1463 occurrenceConfig
.getSpecimenType(), taxon
, taxonName
, occurrenceConfig
.getMatchMode(), null, null,
1464 occurrenceConfig
.getOrderHints(), occurrenceConfig
.getPropertyPaths());
1465 occurrences
.addAll(sobs
);
1466 occurrences
= filterOccurencesByAssignmentAndHierarchy(occurrenceConfig
, occurrences
, taxon
, taxonName
);
1467 return occurrences
.size();
1470 return dao
.countOccurrences(occurrenceConfig
.getClazz(),
1471 occurrenceConfig
.getTitleSearchStringSqlized(), occurrenceConfig
.getSignificantIdentifier(),
1472 occurrenceConfig
.getSpecimenType(), taxon
, taxonName
, occurrenceConfig
.getMatchMode(), null, null,
1473 occurrenceConfig
.getOrderHints(), occurrenceConfig
.getPropertyPaths());
1476 return super.countByTitle(config
);
1481 public Pager
<UuidAndTitleCache
<SpecimenOrObservationBase
>> findByTitleUuidAndTitleCache(
1482 FindOccurrencesConfigurator config
){
1483 List
<UuidAndTitleCache
<SpecimenOrObservationBase
>> occurrences
= new ArrayList
<>();
1485 if(config
.getAssociatedTaxonUuid()!=null){
1486 TaxonBase
<?
> taxonBase
= taxonService
.load(config
.getAssociatedTaxonUuid());
1487 if(taxonBase
!= null && taxonBase
.isInstanceOf(Taxon
.class)){
1488 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
1491 TaxonName taxonName
= null;
1492 if(config
.getAssociatedTaxonNameUuid()!=null){
1493 taxonName
= nameService
.load(config
.getAssociatedTaxonNameUuid());
1495 occurrences
.addAll(dao
.findOccurrencesUuidAndTitleCache(config
.getClazz(),
1496 config
.getTitleSearchString(), config
.getSignificantIdentifier(),
1497 config
.getSpecimenType(), taxon
, taxonName
, config
.getMatchMode(), null, null,
1498 config
.getOrderHints()));
1499 long count
= Integer
.valueOf(occurrences
.size()).longValue();
1500 return new DefaultPagerImpl
<>(config
.getPageNumber(), count
, config
.getPageSize(), occurrences
);
1504 public List
<DerivedUnitDTO
> findByTitleDerivedUnitDTO(FindOccurrencesConfigurator config
) {
1506 if(config
.getAssociatedTaxonUuid()!=null){
1507 TaxonBase
<?
> taxonBase
= taxonService
.load(config
.getAssociatedTaxonUuid());
1508 if(taxonBase
!= null && taxonBase
.isInstanceOf(Taxon
.class)){
1509 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
1512 TaxonName taxonName
= null;
1513 if(config
.getAssociatedTaxonNameUuid()!=null){
1514 taxonName
= nameService
.load(config
.getAssociatedTaxonNameUuid());
1516 List
<DerivedUnit
> occurrences
= new ArrayList
<>();
1517 occurrences
.addAll(dao
.findOccurrences(DerivedUnit
.class,
1518 config
.getTitleSearchString(), config
.getSignificantIdentifier(),
1519 config
.getSpecimenType(), taxon
, taxonName
, config
.getMatchMode(), null, null,
1520 config
.getOrderHints(), null));
1522 List
<DerivedUnitDTO
> dtos
= new ArrayList
<>();
1523 occurrences
.forEach(derivedUnit
->dtos
.add(assembleDerivedUnitDTO(derivedUnit
)));
1528 public <S
extends SpecimenOrObservationBase
> Pager
<S
> findByTitle(
1529 IIdentifiableEntityServiceConfigurator
<S
> config
) {
1530 if (config
instanceof FindOccurrencesConfigurator
) {
1531 FindOccurrencesConfigurator occurrenceConfig
= (FindOccurrencesConfigurator
) config
;
1532 List
<SpecimenOrObservationBase
> occurrences
= new ArrayList
<>();
1534 if(occurrenceConfig
.getAssociatedTaxonUuid()!=null){
1535 TaxonBase
<?
> taxonBase
= taxonService
.load(occurrenceConfig
.getAssociatedTaxonUuid());
1536 if(taxonBase
.isInstanceOf(Taxon
.class)){
1537 taxon
= HibernateProxyHelper
.deproxy(taxonBase
, Taxon
.class);
1540 TaxonName taxonName
= null;
1541 if(occurrenceConfig
.getAssociatedTaxonNameUuid()!=null){
1542 taxonName
= nameService
.load(occurrenceConfig
.getAssociatedTaxonNameUuid());
1544 List
<?
extends SpecimenOrObservationBase
> foundOccurrences
= dao
.findOccurrences(occurrenceConfig
.getClazz(),
1545 occurrenceConfig
.getTitleSearchString(), occurrenceConfig
.getSignificantIdentifier(),
1546 occurrenceConfig
.getSpecimenType(), taxon
, taxonName
, occurrenceConfig
.getMatchMode(), null, null,
1547 occurrenceConfig
.getOrderHints(), occurrenceConfig
.getPropertyPaths());
1548 occurrences
.addAll(foundOccurrences
);
1549 occurrences
= filterOccurencesByAssignmentAndHierarchy(occurrenceConfig
, occurrences
, taxon
, taxonName
);
1551 long count
= Integer
.valueOf(occurrences
.size()).longValue();
1552 return new DefaultPagerImpl
<>(config
.getPageNumber(), count
, config
.getPageSize(), (List
<S
>)occurrences
);
1554 return super.findByTitle(config
);
1557 private List
<SpecimenOrObservationBase
> filterOccurencesByAssignmentAndHierarchy(
1558 FindOccurrencesConfigurator occurrenceConfig
, List
<SpecimenOrObservationBase
> occurrences
, Taxon taxon
,
1559 TaxonName taxonName
) {
1560 //filter out (un-)assigned specimens
1561 if(taxon
==null && taxonName
==null){
1562 AssignmentStatus assignmentStatus
= occurrenceConfig
.getAssignmentStatus();
1563 List
<SpecimenOrObservationBase
> specimenWithAssociations
= new ArrayList
<>();
1564 if(!assignmentStatus
.equals(AssignmentStatus
.ALL_SPECIMENS
)){
1565 for (SpecimenOrObservationBase
<?
> specimenOrObservationBase
: occurrences
) {
1566 boolean includeUnpublished
= true; //TODO not sure if this is correct, maybe we have to propagate publish flag to higher methods.
1567 Collection
<TaxonBase
<?
>> associatedTaxa
= listAssociatedTaxa(specimenOrObservationBase
,
1568 includeUnpublished
, null, null, null, null);
1569 if(!associatedTaxa
.isEmpty()){
1570 specimenWithAssociations
.add(specimenOrObservationBase
);
1574 if(assignmentStatus
.equals(AssignmentStatus
.UNASSIGNED_SPECIMENS
)){
1575 occurrences
.removeAll(specimenWithAssociations
);
1577 if(assignmentStatus
.equals(AssignmentStatus
.ASSIGNED_SPECIMENS
)){
1578 occurrences
= new ArrayList
<>(specimenWithAssociations
);
1581 // indirectly associated specimens
1582 if(occurrenceConfig
.isRetrieveIndirectlyAssociatedSpecimens()){
1583 List
<SpecimenOrObservationBase
> indirectlyAssociatedOccurrences
= new ArrayList
<>(occurrences
);
1584 for (SpecimenOrObservationBase
<?
> specimen
: occurrences
) {
1585 List
<SpecimenOrObservationBase
<?
>> allHierarchyDerivates
= getAllHierarchyDerivatives(specimen
);
1586 for (SpecimenOrObservationBase
<?
> specimenOrObservationBase
: allHierarchyDerivates
) {
1587 if(!occurrences
.contains(specimenOrObservationBase
)){
1588 indirectlyAssociatedOccurrences
.add(specimenOrObservationBase
);
1592 occurrences
= indirectlyAssociatedOccurrences
;
1598 public List
<SpecimenOrObservationBase
<?
>> getAllHierarchyDerivatives(SpecimenOrObservationBase
<?
> specimen
){
1599 List
<SpecimenOrObservationBase
<?
>> allHierarchyDerivatives
= new ArrayList
<>();
1600 Collection
<FieldUnit
> fieldUnits
= findFieldUnits(specimen
.getUuid(), null);
1601 if(fieldUnits
.isEmpty()){
1602 allHierarchyDerivatives
.add(specimen
);
1603 allHierarchyDerivatives
.addAll(getAllChildDerivatives(specimen
));
1606 for (FieldUnit fieldUnit
: fieldUnits
) {
1607 allHierarchyDerivatives
.add(fieldUnit
);
1608 allHierarchyDerivatives
.addAll(getAllChildDerivatives(fieldUnit
));
1611 return allHierarchyDerivatives
;
1615 public List
<DerivedUnit
> getAllChildDerivatives(UUID specimenUuid
){
1616 return getAllChildDerivatives(load(specimenUuid
));
1620 public List
<DerivedUnit
> getAllChildDerivatives(SpecimenOrObservationBase
<?
> specimen
){
1621 if (specimen
== null){
1624 List
<DerivedUnit
> childDerivate
= new ArrayList
<>();
1625 Set
<DerivationEvent
> derivationEvents
= specimen
.getDerivationEvents();
1626 for (DerivationEvent derivationEvent
: derivationEvents
) {
1627 Set
<DerivedUnit
> derivatives
= derivationEvent
.getDerivatives();
1628 for (DerivedUnit derivedUnit
: derivatives
) {
1629 childDerivate
.add(derivedUnit
);
1630 childDerivate
.addAll(getAllChildDerivatives(derivedUnit
.getUuid()));
1633 return childDerivate
;
1637 public long countOccurrences(IIdentifiableEntityServiceConfigurator
<SpecimenOrObservationBase
> config
){
1638 return countByTitle(config
);
1642 public List
<FieldUnit
> findFieldUnitsForGatheringEvent(UUID gatheringEventUuid
) {
1643 return dao
.findFieldUnitsForGatheringEvent(gatheringEventUuid
, null, null, null, null);
1647 public List
<Point
> findPointsForFieldUnitList(List
<UUID
> fieldUnitUuids
) {
1648 return dao
.findPointsForFieldUnitList(fieldUnitUuids
);