1 package eu
.etaxonomy
.cdm
.api
.service
;
3 import java
.math
.BigDecimal
;
4 import java
.util
.ArrayList
;
5 import java
.util
.Arrays
;
6 import java
.util
.Collection
;
7 import java
.util
.Collections
;
8 import java
.util
.HashMap
;
9 import java
.util
.HashSet
;
10 import java
.util
.List
;
12 import java
.util
.Optional
;
14 import java
.util
.UUID
;
15 import java
.util
.stream
.Collectors
;
17 import org
.apache
.log4j
.Logger
;
18 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
19 import org
.springframework
.stereotype
.Service
;
20 import org
.springframework
.transaction
.annotation
.Transactional
;
22 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
23 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteDescriptiveDataSetConfigurator
;
24 import eu
.etaxonomy
.cdm
.api
.service
.config
.IdentifiableServiceConfiguratorImpl
;
25 import eu
.etaxonomy
.cdm
.api
.service
.config
.RemoveDescriptionsFromDescriptiveDataSetConfigurator
;
26 import eu
.etaxonomy
.cdm
.api
.service
.dto
.DescriptionBaseDto
;
27 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
28 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenRowWrapperDTO
;
29 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonRowWrapperDTO
;
30 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
31 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
32 import eu
.etaxonomy
.cdm
.format
.description
.DefaultCategoricalDescriptionBuilder
;
33 import eu
.etaxonomy
.cdm
.format
.description
.DefaultQuantitativeDescriptionBuilder
;
34 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
35 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
36 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
37 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
38 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
39 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
40 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionType
;
41 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
42 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveSystemRole
;
43 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
44 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
45 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKey
;
46 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
47 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
48 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
49 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
50 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
51 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
55 import eu
.etaxonomy
.cdm
.model
.reference
.CdmLinkSource
;
56 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
57 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
58 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
59 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
60 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
61 import eu
.etaxonomy
.cdm
.persistence
.dao
.term
.IDefinedTermDao
;
62 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
63 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
64 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
65 import eu
.etaxonomy
.cdm
.persistence
.dto
.TermDto
;
66 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
67 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
68 import eu
.etaxonomy
.cdm
.strategy
.generate
.PolytomousKeyGenerator
;
69 import eu
.etaxonomy
.cdm
.strategy
.generate
.PolytomousKeyGeneratorConfigurator
;
72 @Transactional(readOnly
=true)
73 public class DescriptiveDataSetService
74 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
75 implements IDescriptiveDataSetService
{
77 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
80 private IOccurrenceService occurrenceService
;
83 private ITaxonService taxonService
;
86 private IPolytomousKeyService polytomousKeyService
;
89 private IDefinedTermDao termDao
;
92 private IDescriptionService descriptionService
;
95 private ITaxonNodeService taxonNodeService
;
99 protected void setDao(IDescriptiveDataSetDao dao
) {
104 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
105 List
<String
> propertyPaths
) {
106 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
110 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
111 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
112 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
116 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
117 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
121 public ArrayList
<RowWrapperDTO
> getRowWrapper(UUID descriptiveDataSetUuid
, IProgressMonitor monitor
) {
122 DescriptiveDataSet descriptiveDataSet
= load(descriptiveDataSetUuid
);
123 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
124 ArrayList
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
125 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
126 for (DescriptionBase description
: descriptions
) {
127 if(monitor
.isCanceled()){
128 return new ArrayList
<>();
130 RowWrapperDTO rowWrapper
= null;
131 // only viable descriptions are aggregated, literature or default descriptions
132 if(HibernateProxyHelper
.isInstanceOf(description
, TaxonDescription
.class) &&
133 (description
.isAggregatedStructuredDescription()
134 || description
.getTypes().contains(DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
)
135 || description
.getTypes().contains(DescriptionType
.SECONDARY_DATA
)
137 rowWrapper
= createTaxonRowWrapper(description
.getUuid(), descriptiveDataSet
.getUuid());
139 else if (HibernateProxyHelper
.isInstanceOf(description
, SpecimenDescription
.class)&&
140 !description
.getTypes().contains(DescriptionType
.CLONE_FOR_SOURCE
)){
141 rowWrapper
= createSpecimenRowWrapper(HibernateProxyHelper
.deproxy(description
, SpecimenDescription
.class), descriptiveDataSetUuid
);
143 if(rowWrapper
!=null){
144 wrappers
.add(rowWrapper
);
152 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
153 List
<UUID
> filteredNodes
= findFilteredTaxonNodes(descriptiveDataSet
);
154 if(filteredNodes
.isEmpty()){
155 return Collections
.EMPTY_SET
;
157 Collection
<SpecimenNodeWrapper
> result
= occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
163 public Collection
<SpecimenNodeWrapper
> loadSpecimens(UUID descriptiveDataSetUuid
){
164 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
165 return loadSpecimens(dataSet
);
170 public List
<UUID
> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
){
171 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
172 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
173 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
174 filter
.setIncludeUnpublished(true);
176 return taxonNodeService
.uuidList(filter
);
180 public List
<TaxonNode
> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
, List
<String
> propertyPaths
){
181 return taxonNodeService
.load(findFilteredTaxonNodes(descriptiveDataSet
), propertyPaths
);
185 public TaxonDescription
findDefaultDescription(UUID specimenDescriptionUuid
, UUID dataSetUuid
){
186 SpecimenDescription specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescriptionUuid
);
187 DescriptiveDataSet dataSet
= load(dataSetUuid
);
188 TaxonNode node
= findTaxonNodeForDescription(specimenDescription
, dataSet
);
189 return recurseDefaultDescription(node
, dataSet
);
192 private TaxonDescription
recurseDefaultDescription(TaxonNode node
, DescriptiveDataSet dataSet
){
193 TaxonDescription defaultDescription
= null;
194 if(node
!=null && node
.getTaxon()!=null){
195 defaultDescription
= findTaxonDescriptionByDescriptionType(dataSet
, node
.getTaxon(), DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
);
196 if(defaultDescription
==null && node
.getParent()!=null){
197 defaultDescription
= recurseDefaultDescription(node
.getParent(), dataSet
);
200 return defaultDescription
;
203 private TaxonNode
findTaxonNodeForDescription(SpecimenDescription description
, DescriptiveDataSet descriptiveDataSet
){
204 SpecimenOrObservationBase specimen
= description
.getDescribedSpecimenOrObservation();
207 Set
<IndividualsAssociation
> associations
= (Set
<IndividualsAssociation
>)descriptiveDataSet
.getDescriptions()
209 .flatMap(desc
->desc
.getElements().stream())// put all description element in one stream
210 .filter(element
->element
instanceof IndividualsAssociation
)
211 .map(ia
->(IndividualsAssociation
)ia
)
212 .collect(Collectors
.toSet());
213 Classification classification
= descriptiveDataSet
.getTaxonSubtreeFilter().iterator().next().getClassification();
214 for (IndividualsAssociation individualsAssociation
: associations
) {
215 if(individualsAssociation
.getAssociatedSpecimenOrObservation().equals(specimen
)){
216 return ((TaxonDescription
) individualsAssociation
.getInDescription()).getTaxon().getTaxonNode(classification
);
223 public TaxonRowWrapperDTO
createTaxonRowWrapper(UUID taxonDescriptionUuid
, UUID descriptiveDataSetUuid
) {
224 TaxonNode taxonNode
= null;
225 Classification classification
= null;
226 TaxonDescription description
= (TaxonDescription
) descriptionService
.load(taxonDescriptionUuid
,
227 Arrays
.asList("taxon", "descriptionElements", "descriptionElements.feature"));
228 DescriptiveDataSet descriptiveDataSet
= dao
.load(descriptiveDataSetUuid
, null);
229 Optional
<TaxonNode
> first
= descriptiveDataSet
.getTaxonSubtreeFilter().stream()
230 .filter(node
->node
.getClassification()!=null).findFirst();
231 Optional
<Classification
> classificationOptional
= first
.map(node
->node
.getClassification());
232 Set
<DescriptionBaseDto
> descriptions
= new HashSet
<>();
233 if(classificationOptional
.isPresent()){
234 classification
= classificationOptional
.get();
235 Taxon taxon
= (Taxon
) taxonService
.load(description
.getTaxon().getId(), Arrays
.asList("taxonNodes", "taxonNodes.classification"));
236 taxonNode
= taxon
.getTaxonNode(classification
);
237 for (DescriptionBase desc
: taxon
.getDescriptions()){
238 descriptions
.add(new DescriptionBaseDto(desc
));
243 return new TaxonRowWrapperDTO(new DescriptionBaseDto(description
), new TaxonNodeDto(taxonNode
), descriptions
);
247 @Transactional(readOnly
=false)
248 public UpdateResult
addRowWrapperToDataset(Collection
<SpecimenRowWrapperDTO
> wrappers
, UUID datasetUuid
){
249 UpdateResult result
= new UpdateResult();
250 DescriptiveDataSet dataSet
= load(datasetUuid
);
251 result
.setCdmEntity(dataSet
);
253 List
<UUID
> taxonUuids
= wrappers
.stream().map(wrapper
->wrapper
.getTaxonNode().getTaxonUuid()).collect(Collectors
.toList());
254 List
<TaxonBase
> taxa
= taxonService
.load(taxonUuids
, Arrays
.asList(new String
[]{"descriptions"}));
256 for (SpecimenRowWrapperDTO wrapper
: wrappers
) {
257 Optional
<TaxonBase
> findAny
= taxa
.stream().filter(taxon
->taxon
.getUuid().equals(wrapper
.getTaxonNode().getTaxonUuid())).findAny();
258 if(!findAny
.isPresent()){
259 result
.addException(new IllegalArgumentException("Could not create wrapper for "+ wrapper
.getSpecimen().getLabel()));
262 Taxon taxon
= (Taxon
) findAny
.get();
263 // UUID taxonDescriptionUuid = wrapper..getTaxonDescriptionUuid();
264 TaxonDescription taxonDescription
= null;
265 // if(taxonDescriptionUuid!=null){
266 // taxonDescription = (TaxonDescription) descriptionService.load(taxonDescriptionUuid);
268 SpecimenOrObservationBase specimen
= occurrenceService
.load(wrapper
.getSpecimen().getUuid());
269 if(taxonDescription
==null){
270 Optional
<TaxonDescription
> associationDescriptionOptional
= taxon
.getDescriptions().stream()
271 .filter(desc
->desc
.getTypes().contains(DescriptionType
.INDIVIDUALS_ASSOCIATION
))
273 if(!associationDescriptionOptional
.isPresent()){
274 taxonDescription
= TaxonDescription
.NewInstance(taxon
);
275 taxonDescription
.addType(DescriptionType
.INDIVIDUALS_ASSOCIATION
);
278 taxonDescription
= associationDescriptionOptional
.get();
282 IndividualsAssociation association
= IndividualsAssociation
.NewInstance(specimen
);
283 taxonDescription
.addElement(association
);
284 taxonService
.saveOrUpdate(taxon
);
285 result
.addUpdatedObject(taxon
);
287 DescriptionBase specimenDescription
= wrapper
.getDescription().getDescription();
288 if (specimenDescription
.isPersited()){
289 specimenDescription
= descriptionService
.load(specimenDescription
.getUuid());
291 specimen
.addDescription(specimenDescription
);
293 // SpecimenRowWrapperDTO rowWrapper = createSpecimenRowWrapper(specimenDescription, wrapper.getTaxonNode().getUuid(), datasetUuid);
295 result
.addException(new IllegalArgumentException("Could not create wrapper for "+wrapper
.getDescription().getDescription()));
298 //add specimen description to data set
301 // specimenDescription = (SpecimenDescription) descriptionService.load(rowWrapper.getDescription().getDescription().getUuid());
303 specimenDescription
.addDescriptiveDataSet(dataSet
);
304 //add taxon description with IndividualsAssociation to the specimen to data set
305 taxonDescription
.addDescriptiveDataSet(dataSet
);
306 result
.addUpdatedObject(specimen
);
307 result
.addUpdatedObject(specimenDescription
);
308 result
.addUpdatedObject(taxonDescription
);
310 saveOrUpdate(dataSet
);
314 private SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, UUID taxonNodeUuid
,
316 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
);
317 DescriptiveDataSet descriptiveDataSet
= load(datasetUuid
);
318 SpecimenOrObservationBase specimen
= HibernateProxyHelper
.deproxy(description
.getDescribedSpecimenOrObservation(), SpecimenOrObservationBase
.class);
319 //supplemental information
321 taxonNode
= findTaxonNodeForDescription(description
, descriptiveDataSet
);
323 FieldUnit fieldUnit
= null;
324 String identifier
= null;
325 NamedArea country
= null;
329 //taxon node was found
332 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
333 Arrays
.asList(new String
[]{
335 "gatheringEvent.country"
337 if(fieldUnits
.size()>1){
338 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
342 if (fieldUnits
!= null && fieldUnits
.size()>0){
343 fieldUnit
= fieldUnits
.iterator().next();
347 if(HibernateProxyHelper
.isInstanceOf(specimen
, DerivedUnit
.class)){
348 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
351 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
352 country
= fieldUnit
.getGatheringEvent().getCountry();
354 //get default taxon description
355 // TaxonDescription defaultTaxonDescription = findDefaultDescription(description.getUuid(), descriptiveDataSet.getUuid());
356 TaxonDescription defaultTaxonDescription
= recurseDefaultDescription(taxonNode
, descriptiveDataSet
);
357 TaxonRowWrapperDTO taxonRowWrapper
= defaultTaxonDescription
!= null
358 ?
createTaxonRowWrapper(defaultTaxonDescription
.getUuid(), descriptiveDataSet
.getUuid()) : null;
359 // use description not specimen for specimenRow
360 SpecimenRowWrapperDTO specimenRowWrapperDTO
= new SpecimenRowWrapperDTO(new DescriptionBaseDto(description
), specimen
.getRecordBasis(), new TaxonNodeDto(taxonNode
), fieldUnit
, identifier
, country
);
361 specimenRowWrapperDTO
.setDefaultDescription(taxonRowWrapper
);
362 return specimenRowWrapperDTO
;
366 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, UUID descriptiveDataSetUuid
){
367 return createSpecimenRowWrapper(description
, null, descriptiveDataSetUuid
);
371 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(UUID specimenUuid
, UUID taxonNodeUuid
, UUID descriptiveDataSetUuid
){
373 SpecimenOrObservationBase
<?
> specimen
= occurrenceService
.load(specimenUuid
);
374 SpecimenDescription specimenDescription
= findSpecimenDescription(descriptiveDataSetUuid
, specimen
, true);
375 return createSpecimenRowWrapper(specimenDescription
, taxonNodeUuid
, descriptiveDataSetUuid
);
379 @Transactional(readOnly
= false)
380 public UpdateResult
updateCaches(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
381 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
383 clazz
= DescriptiveDataSet
.class;
385 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
388 private TaxonDescription
findTaxonDescriptionByDescriptionType(DescriptiveDataSet dataSet
, Taxon taxon
, DescriptionType descriptionType
){
389 Optional
<TaxonDescription
> first
= taxon
.getDescriptions().stream()
390 .filter(desc
-> desc
.getTypes().stream().anyMatch(type
-> type
.equals(descriptionType
)))
391 .filter(desc
-> dataSet
.getDescriptions().contains(desc
))
393 if(first
.isPresent()){
394 return HibernateProxyHelper
.deproxy(descriptionService
.load(first
.get().getUuid(),
395 Arrays
.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription
.class);
401 public TaxonDescription
findTaxonDescriptionByDescriptionType(UUID dataSetUuid
, UUID taxonNodeUuid
, DescriptionType descriptionType
){
402 DescriptiveDataSet dataSet
= load(dataSetUuid
);
403 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
);
404 return findTaxonDescriptionByDescriptionType(dataSet
, taxonNode
.getTaxon(), descriptionType
);
408 @Transactional(readOnly
=false)
409 public UpdateResult
generatePolytomousKey(UUID descriptiveDataSetUuid
, UUID taxonUuid
) {
410 UpdateResult result
= new UpdateResult();
412 PolytomousKeyGeneratorConfigurator keyConfig
= new PolytomousKeyGeneratorConfigurator();
413 DescriptiveDataSet descriptiveDataSet
= load(descriptiveDataSetUuid
);
414 keyConfig
.setDataSet(descriptiveDataSet
);
415 PolytomousKey key
= new PolytomousKeyGenerator().invoke(keyConfig
);
416 IdentifiableServiceConfiguratorImpl
<PolytomousKey
> serviceConfig
= new IdentifiableServiceConfiguratorImpl
<>();
417 serviceConfig
.setTitleSearchString(descriptiveDataSet
.getTitleCache());
418 List
<PolytomousKey
> list
= polytomousKeyService
.findByTitle(serviceConfig
).getRecords();
420 list
.forEach(polytomousKey
->polytomousKeyService
.delete(polytomousKey
));
422 key
.setTitleCache(descriptiveDataSet
.getTitleCache(), true);
424 Taxon taxon
= (Taxon
) taxonService
.load(taxonUuid
);
425 key
.addTaxonomicScope(taxon
);
427 polytomousKeyService
.saveOrUpdate(key
);
429 result
.setCdmEntity(key
);
430 result
.addUpdatedObject(taxon
);
435 @Transactional(readOnly
=false)
436 public DeleteResult
removeDescription(UUID descriptionUuid
, UUID descriptiveDataSetUuid
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
437 DeleteResult result
= new DeleteResult();
438 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
439 DescriptionBase descriptionBase
= descriptionService
.load(descriptionUuid
);
440 if(dataSet
==null || descriptionBase
==null){
444 removeDescriptionFromDataSet(result
, dataSet
, descriptionBase
, config
);
451 @Transactional(readOnly
=false)
452 public DeleteResult
removeDescriptions(List
<UUID
> descriptionUuids
, UUID descriptiveDataSetUuid
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
453 DeleteResult result
= new DeleteResult();
454 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
455 List
<DescriptionBase
> descriptions
= descriptionService
.load(descriptionUuids
, null);
456 if(dataSet
==null || descriptions
==null){
460 for (DescriptionBase description
: descriptions
){
461 removeDescriptionFromDataSet(result
, dataSet
, description
, config
);
474 private void removeDescriptionFromDataSet(DeleteResult result
, DescriptiveDataSet dataSet
,
475 DescriptionBase description
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
476 if (description
== null){
479 boolean success
= dataSet
.removeDescription(description
);
480 result
.addDeletedObject(description
);// remove taxon description with IndividualsAssociation from data set
481 if(description
instanceof SpecimenDescription
){
482 @SuppressWarnings("cast")
483 Set
<IndividualsAssociation
> associations
= (Set
<IndividualsAssociation
>)dataSet
.getDescriptions()
485 .flatMap(desc
->desc
.getElements().stream())// put all description element in one stream
486 .filter(element
->element
instanceof IndividualsAssociation
)
487 .map(ia
->(IndividualsAssociation
)ia
)
488 .collect(Collectors
.toSet());
489 Classification classification
= dataSet
.getTaxonSubtreeFilter().iterator().next().getClassification();
490 for (IndividualsAssociation individualsAssociation
: associations
) {
491 if(individualsAssociation
.getAssociatedSpecimenOrObservation().equals(description
.getDescribedSpecimenOrObservation())){
492 dataSet
.removeDescription(individualsAssociation
.getInDescription());
493 result
.addUpdatedObject(individualsAssociation
.getInDescription());
497 if (description
instanceof TaxonDescription
){
498 UUID taxonUuid
= ((TaxonDescription
)description
).getTaxon().getUuid();
499 DeleteResult isDeletable
= descriptionService
.isDeletable(description
.getUuid());
500 for (CdmBase relatedCdmBase
: isDeletable
.getRelatedObjects()){
501 if (relatedCdmBase
instanceof CdmLinkSource
){
502 CdmLinkSource linkSource
= (CdmLinkSource
)relatedCdmBase
;
503 if (linkSource
.getTarget().equals(this)){
511 if (!config
.isOnlyRemoveDescriptionsFromDataSet()){
512 DeleteResult deleteResult
= descriptionService
.deleteDescription(description
);
513 result
.includeResult(deleteResult
);
514 result
.addUpdatedObject(dataSet
);
516 MergeResult
<DescriptiveDataSet
> mergeResult
= dao
.merge(dataSet
, true);
517 result
.addUpdatedObject(mergeResult
.getMergedEntity());
520 result
.setStatus(success?Status
.OK
:Status
.ERROR
);
524 @Transactional(readOnly
= false)
525 public DeleteResult
delete(UUID datasetUuid
, DeleteDescriptiveDataSetConfigurator config
, IProgressMonitor monitor
){
526 DescriptiveDataSet dataSet
= dao
.load(datasetUuid
);
527 monitor
.beginTask("Delete Descriptive Dataset", dataSet
.getDescriptions().size() +1);
529 DeleteResult result
= new DeleteResult();
530 DeleteResult descriptionResult
= new DeleteResult();
531 if (!dataSet
.getDescriptions().isEmpty()){
532 Set
<DescriptionBase
> descriptions
= new HashSet();;
533 for (DescriptionBase desc
: dataSet
.getDescriptions()){
534 descriptions
.add(desc
);
536 monitor
.subTask("Delete descriptions");
537 for (DescriptionBase desc
: descriptions
){
538 dataSet
.removeDescription(desc
);
539 if (desc
instanceof SpecimenDescription
&& config
.isDeleteAllSpecimenDescriptions()){
540 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
541 }else if (desc
instanceof TaxonDescription
){
542 if( desc
.getTypes().contains(DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
) && config
.isDeleteAllDefaultDescriptions()){
543 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
544 }else if (desc
.getTypes().contains(DescriptionType
.SECONDARY_DATA
) && config
.isDeleteAllLiteratureDescriptions()){
545 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
546 }else if (desc
.getTypes().contains(DescriptionType
.AGGREGATED_STRUC_DESC
) && config
.isDeleteAllAggregatedDescriptions()){
547 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
556 UUID uuid
= dao
.delete(dataSet
);
559 result
.includeResult(descriptionResult
);
560 result
.setStatus(Status
.OK
);
561 result
.addDeletedObject(dataSet
);
566 @Transactional(readOnly
=false)
567 public TaxonRowWrapperDTO
createTaxonDescription(UUID dataSetUuid
, UUID taxonNodeUuid
, DescriptionType descriptionType
){
568 DescriptiveDataSet dataSet
= load(dataSetUuid
);
569 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
, Arrays
.asList("taxon"));
570 TaxonDescription newTaxonDescription
= TaxonDescription
.NewInstance(taxonNode
.getTaxon());
571 newTaxonDescription
.setTitleCache(dataSet
.getLabel()+": "+newTaxonDescription
.generateTitle(), true); //$NON-NLS-2$
572 newTaxonDescription
.getTypes().add(descriptionType
);
574 dataSet
.getDescriptiveSystem().getDistinctTerms().forEach(wsFeature
->{
575 if(wsFeature
.isSupportsCategoricalData()){
576 newTaxonDescription
.addElement(CategoricalData
.NewInstance(wsFeature
));
578 else if(wsFeature
.isSupportsQuantitativeData()){
579 newTaxonDescription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
582 dataSet
.addDescription(newTaxonDescription
);
583 saveOrUpdate(dataSet
);
584 return createTaxonRowWrapper(newTaxonDescription
.getUuid(), dataSet
.getUuid());
588 public List
<TermDto
> getSupportedStatesForFeature(UUID featureUuid
){
589 return termDao
.getSupportedStatesForFeature(featureUuid
);
593 @Transactional(readOnly
=false)
594 public SpecimenDescription
findSpecimenDescription(UUID descriptiveDataSetUuid
, SpecimenOrObservationBase specimen
, boolean addDatasetSource
){
595 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
596 // SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
598 Set
<?
extends Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctTerms();
599 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
601 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
602 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
604 //check if description is already added to data set
605 if(dataSet
.getDescriptions().contains(specimenDescription
) ){
606 return specimenDescription
;
609 //gather specimen description features and check for match with dataset features
610 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
611 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
612 Feature feature
= specimenDescriptionElement
.getFeature();
613 specimenDescriptionFeatures
.add(feature
);
614 if(datasetFeatures
.contains(feature
) && RowWrapperDTO
.hasData(specimenDescriptionElement
)){
615 matchingDescriptionElements
.add(specimenDescriptionElement
);
619 //Create new specimen description if description has not already been added to the dataset
620 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
621 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
623 //check for equals description element (same feature and same values)
624 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
625 for(DescriptionElementBase element
:matchingDescriptionElements
){
626 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
628 list
= new ArrayList
<>();
631 featureToElementMap
.put(element
.getFeature(), list
);
633 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
634 for(Feature feature
:featureToElementMap
.keySet()){
635 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
636 //no duplicate description elements found for this feature
637 if(elements
.size()==1){
638 descriptionElementsToClone
.add(elements
.get(0));
640 //duplicates found -> check if all are equal
642 DescriptionElementBase match
= null;
643 for (DescriptionElementBase descriptionElementBase
: elements
) {
645 match
= descriptionElementBase
;
647 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
649 //TODO: propagate message
650 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
651 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
656 descriptionElementsToClone
.add(match
);
660 //clone matching descriptionElements
661 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
662 DescriptionElementBase clone
;
664 clone
= descriptionElementBase
.clone(newDesription
);
665 clone
.getSources().forEach(source
-> {
666 if(descriptionElementBase
instanceof CategoricalData
){
667 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, Arrays
.asList(new Language
[]{Language
.DEFAULT()}));
668 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
670 else if(descriptionElementBase
instanceof QuantitativeData
){
671 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, Arrays
.asList(new Language
[]{Language
.DEFAULT()}));
672 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
675 } catch (CloneNotSupportedException e
) {
680 //add sources of data set
681 if(addDatasetSource
){
682 dataSet
.getSources().forEach(source
->{
684 newDesription
.addSource(source
.clone());
685 } catch (CloneNotSupportedException e
) {
690 return newDesription
;
694 //TODO: this should either be solved in the model class itself
695 //OR this should cover all possibilities including modifiers for example
696 private class DescriptionElementCompareWrapper
{
698 private DescriptionElementBase element
;
699 private Set
<UUID
> stateUuids
= new HashSet
<>();
700 private Set
<BigDecimal
> avgs
= new HashSet
<>();
701 private Set
<BigDecimal
> exacts
= new HashSet
<>();
702 private Set
<BigDecimal
> maxs
= new HashSet
<>();
703 private Set
<BigDecimal
> mins
= new HashSet
<>();
704 private Set
<BigDecimal
> sampleSizes
= new HashSet
<>();
705 private Set
<BigDecimal
> standardDevs
= new HashSet
<>();
706 private Set
<BigDecimal
> lowerBounds
= new HashSet
<>();
707 private Set
<BigDecimal
> upperBounds
= new HashSet
<>();
708 private Set
<BigDecimal
> variances
= new HashSet
<>();
710 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
711 this.element
= element
;
712 if(element
.isInstanceOf(CategoricalData
.class)){
713 CategoricalData elementData
= (CategoricalData
)element
;
714 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
716 else if(element
.isInstanceOf(QuantitativeData
.class)){
717 QuantitativeData elementData
= (QuantitativeData
)element
;
718 elementData
.getStatisticalValues().forEach(value
->{
719 if(value
.getType().equals(StatisticalMeasure
.AVERAGE())){
720 avgs
.add(value
.getValue());
722 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
723 exacts
.add(value
.getValue());
726 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
727 maxs
.add(value
.getValue());
729 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
730 mins
.add(value
.getValue());
732 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
733 sampleSizes
.add(value
.getValue());
736 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
737 standardDevs
.add(value
.getValue());
739 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
740 lowerBounds
.add(value
.getValue());
743 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
744 upperBounds
.add(value
.getValue());
746 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
747 variances
.add(value
.getValue());
754 public int hashCode() {
755 final int prime
= 31;
757 result
= prime
* result
+ getOuterType().hashCode();
758 result
= prime
* result
+ ((avgs
== null) ?
0 : avgs
.hashCode());
759 result
= prime
* result
+ ((element
== null) ?
0 : element
.hashCode());
760 result
= prime
* result
+ ((exacts
== null) ?
0 : exacts
.hashCode());
761 result
= prime
* result
+ ((lowerBounds
== null) ?
0 : lowerBounds
.hashCode());
762 result
= prime
* result
+ ((maxs
== null) ?
0 : maxs
.hashCode());
763 result
= prime
* result
+ ((mins
== null) ?
0 : mins
.hashCode());
764 result
= prime
* result
+ ((sampleSizes
== null) ?
0 : sampleSizes
.hashCode());
765 result
= prime
* result
+ ((standardDevs
== null) ?
0 : standardDevs
.hashCode());
766 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
767 result
= prime
* result
+ ((upperBounds
== null) ?
0 : upperBounds
.hashCode());
768 result
= prime
* result
+ ((variances
== null) ?
0 : variances
.hashCode());
773 public boolean equals(Object obj
) {
780 if (getClass() != obj
.getClass()) {
783 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
784 if (!getOuterType().equals(other
.getOuterType())) {
788 if (other
.avgs
!= null) {
791 } else if (!avgs
.equals(other
.avgs
)) {
794 if (element
== null) {
795 if (other
.element
!= null) {
798 } else if (!element
.equals(other
.element
)) {
801 if (exacts
== null) {
802 if (other
.exacts
!= null) {
805 } else if (!exacts
.equals(other
.exacts
)) {
808 if (lowerBounds
== null) {
809 if (other
.lowerBounds
!= null) {
812 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
816 if (other
.maxs
!= null) {
819 } else if (!maxs
.equals(other
.maxs
)) {
823 if (other
.mins
!= null) {
826 } else if (!mins
.equals(other
.mins
)) {
829 if (sampleSizes
== null) {
830 if (other
.sampleSizes
!= null) {
833 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
836 if (standardDevs
== null) {
837 if (other
.standardDevs
!= null) {
840 } else if (!standardDevs
.equals(other
.standardDevs
)) {
843 if (stateUuids
== null) {
844 if (other
.stateUuids
!= null) {
847 } else if (!stateUuids
.equals(other
.stateUuids
)) {
850 if (upperBounds
== null) {
851 if (other
.upperBounds
!= null) {
854 } else if (!upperBounds
.equals(other
.upperBounds
)) {
857 if (variances
== null) {
858 if (other
.variances
!= null) {
861 } else if (!variances
.equals(other
.variances
)) {
867 private DescriptiveDataSetService
getOuterType() {
868 return DescriptiveDataSetService
.this;