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
.CategoricalDataDto
;
27 import eu
.etaxonomy
.cdm
.api
.service
.dto
.DescriptionBaseDto
;
28 import eu
.etaxonomy
.cdm
.api
.service
.dto
.DescriptionElementDto
;
29 import eu
.etaxonomy
.cdm
.api
.service
.dto
.QuantitativeDataDto
;
30 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
31 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenOrObservationDTOFactory
;
32 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenRowWrapperDTO
;
33 import eu
.etaxonomy
.cdm
.api
.service
.dto
.StateDataDto
;
34 import eu
.etaxonomy
.cdm
.api
.service
.dto
.StatisticalMeasurementValueDto
;
35 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonRowWrapperDTO
;
36 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
37 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
38 import eu
.etaxonomy
.cdm
.format
.description
.DefaultCategoricalDescriptionBuilder
;
39 import eu
.etaxonomy
.cdm
.format
.description
.DefaultQuantitativeDescriptionBuilder
;
40 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
41 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
42 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
43 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
44 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
45 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
46 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionType
;
47 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
48 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveSystemRole
;
49 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
50 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
51 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
52 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKey
;
53 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
54 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
55 import eu
.etaxonomy
.cdm
.model
.description
.State
;
56 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
57 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasurementValue
;
58 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
59 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
60 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
61 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
62 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
63 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
64 import eu
.etaxonomy
.cdm
.model
.reference
.CdmLinkSource
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
67 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
68 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
69 import eu
.etaxonomy
.cdm
.model
.term
.DefinedTermBase
;
70 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
71 import eu
.etaxonomy
.cdm
.persistence
.dao
.term
.IDefinedTermDao
;
72 import eu
.etaxonomy
.cdm
.persistence
.dto
.DescriptiveDataSetBaseDto
;
73 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
74 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
75 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
76 import eu
.etaxonomy
.cdm
.persistence
.dto
.TermDto
;
77 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
78 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
79 import eu
.etaxonomy
.cdm
.strategy
.generate
.PolytomousKeyGenerator
;
80 import eu
.etaxonomy
.cdm
.strategy
.generate
.PolytomousKeyGeneratorConfigurator
;
83 @Transactional(readOnly
=true)
84 public class DescriptiveDataSetService
85 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
86 implements IDescriptiveDataSetService
{
88 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
91 private IOccurrenceService occurrenceService
;
94 private ITaxonService taxonService
;
97 private IPolytomousKeyService polytomousKeyService
;
100 private IDefinedTermDao termDao
;
103 private IDescriptionService descriptionService
;
106 private ITaxonNodeService taxonNodeService
;
110 protected void setDao(IDescriptiveDataSetDao dao
) {
115 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
116 List
<String
> propertyPaths
) {
117 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
121 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
122 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
123 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
127 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
128 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
132 public List
<RowWrapperDTO
<?
>> getRowWrapper(UUID descriptiveDataSetUuid
, IProgressMonitor monitor
) {
133 DescriptiveDataSet descriptiveDataSet
= load(descriptiveDataSetUuid
);
134 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
135 List
<RowWrapperDTO
<?
>> wrappers
= new ArrayList
<>();
136 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
137 for (DescriptionBase
<?
> description
: descriptions
) {
138 if(monitor
.isCanceled()){
139 return new ArrayList
<>();
141 RowWrapperDTO
<?
> rowWrapper
= null;
142 // only viable descriptions are aggregated, literature or default descriptions
143 if(HibernateProxyHelper
.isInstanceOf(description
, TaxonDescription
.class) &&
144 (description
.isAggregatedStructuredDescription()
145 || description
.getTypes().contains(DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
)
146 || description
.getTypes().contains(DescriptionType
.SECONDARY_DATA
)
148 rowWrapper
= createTaxonRowWrapper((TaxonDescription
)description
, descriptiveDataSet
.getUuid());
150 else if (HibernateProxyHelper
.isInstanceOf(description
, SpecimenDescription
.class)&&
151 !description
.getTypes().contains(DescriptionType
.CLONE_FOR_SOURCE
)){
152 rowWrapper
= createSpecimenRowWrapper(HibernateProxyHelper
.deproxy(description
, SpecimenDescription
.class), descriptiveDataSetUuid
);
154 if(rowWrapper
!=null){
155 wrappers
.add(rowWrapper
);
163 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
164 List
<UUID
> filteredNodes
= findFilteredTaxonNodes(descriptiveDataSet
);
165 if(filteredNodes
.isEmpty()){
166 return Collections
.emptySet();
168 Collection
<SpecimenNodeWrapper
> result
= occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
174 public Collection
<SpecimenNodeWrapper
> loadSpecimens(UUID descriptiveDataSetUuid
){
175 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
176 return loadSpecimens(dataSet
);
181 public List
<UUID
> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
){
182 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
183 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
184 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
185 filter
.setIncludeUnpublished(true);
187 return taxonNodeService
.uuidList(filter
);
191 public List
<TaxonNode
> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
, List
<String
> propertyPaths
){
192 return taxonNodeService
.load(findFilteredTaxonNodes(descriptiveDataSet
), propertyPaths
);
196 public TaxonDescription
findDefaultDescription(UUID specimenDescriptionUuid
, UUID dataSetUuid
){
197 SpecimenDescription specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescriptionUuid
);
198 DescriptiveDataSet dataSet
= load(dataSetUuid
);
199 TaxonNode node
= findTaxonNodeForDescription(specimenDescription
, dataSet
);
200 return recurseDefaultDescription(node
, dataSet
);
203 private TaxonDescription
recurseDefaultDescription(TaxonNode node
, DescriptiveDataSet dataSet
){
204 TaxonDescription defaultDescription
= null;
205 if(node
!=null && node
.getTaxon()!=null){
206 defaultDescription
= findTaxonDescriptionByDescriptionType(dataSet
, node
.getTaxon(), DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
);
207 if(defaultDescription
==null && node
.getParent()!=null){
208 defaultDescription
= recurseDefaultDescription(node
.getParent(), dataSet
);
211 return defaultDescription
;
214 private TaxonNode
findTaxonNodeForDescription(SpecimenDescription description
, DescriptiveDataSet descriptiveDataSet
){
215 SpecimenOrObservationBase
<?
> specimen
= description
.getDescribedSpecimenOrObservation();
218 @SuppressWarnings({ "unchecked", "cast" })
219 //NOTE: don't remove cast as it does not compile on some systems
220 Set
<IndividualsAssociation
> associations
= (Set
<IndividualsAssociation
>)descriptiveDataSet
.getDescriptions()
222 .flatMap(desc
->desc
.getElements().stream())// put all description element in one stream
223 .filter(element
->element
instanceof IndividualsAssociation
)
224 .map(ia
->(IndividualsAssociation
)ia
)
225 .collect(Collectors
.toSet());
226 Classification classification
= descriptiveDataSet
.getTaxonSubtreeFilter().iterator().next().getClassification();
227 for (IndividualsAssociation individualsAssociation
: associations
) {
228 if(individualsAssociation
.getAssociatedSpecimenOrObservation().equals(specimen
)){
229 return ((TaxonDescription
) individualsAssociation
.getInDescription()).getTaxon().getTaxonNode(classification
);
236 public TaxonRowWrapperDTO
createTaxonRowWrapper(TaxonDescription taxonDescription
, UUID descriptiveDataSetUuid
) {
237 Classification classification
= null;
238 DescriptionBaseDto description
= DescriptionBaseDto
.fromDescription(taxonDescription
);
240 DescriptiveDataSet descriptiveDataSet
= dao
.load(descriptiveDataSetUuid
, null);
241 Optional
<TaxonNode
> first
= descriptiveDataSet
.getTaxonSubtreeFilter().stream()
242 .filter(node
->node
.getClassification()!=null).findFirst();
243 Optional
<Classification
> classificationOptional
= first
.map(node
->node
.getClassification());
244 Set
<DescriptionBaseDto
> descriptions
= new HashSet
<>();
245 TaxonNodeDto nodeDto
= null;
246 if(classificationOptional
.isPresent()){
247 classification
= classificationOptional
.get();
248 nodeDto
= taxonNodeService
.dto(description
.getTaxonDto().getUuid(), classification
.getUuid());
251 return new TaxonRowWrapperDTO(description
, nodeDto
, descriptions
);
255 @Transactional(readOnly
=false)
256 public UpdateResult
addRowWrapperToDataset(Collection
<SpecimenRowWrapperDTO
> wrappers
, UUID datasetUuid
){
257 UpdateResult result
= new UpdateResult();
258 DescriptiveDataSet dataSet
= load(datasetUuid
);
259 result
.setCdmEntity(dataSet
);
261 List
<UUID
> taxonUuids
= wrappers
.stream().map(wrapper
->wrapper
.getTaxonNode().getTaxonUuid()).collect(Collectors
.toList());
262 List
<TaxonBase
> taxa
= taxonService
.load(taxonUuids
, Arrays
.asList(new String
[]{"descriptions"}));
264 for (SpecimenRowWrapperDTO wrapper
: wrappers
) {
265 Optional
<TaxonBase
> findAny
= taxa
.stream().filter(taxon
->taxon
.getUuid().equals(wrapper
.getTaxonNode().getTaxonUuid())).findAny();
266 if(!findAny
.isPresent()){
267 result
.addException(new IllegalArgumentException("Could not create wrapper for "+ wrapper
.getSpecimenDto().getLabel()));
270 Taxon taxon
= (Taxon
) findAny
.get();
272 SpecimenOrObservationBase
<?
> specimen
= occurrenceService
.load(wrapper
.getSpecimenDto().getUuid());
274 TaxonDescription taxonDescription
= taxon
.getDescriptions().stream()
275 .filter(desc
->desc
.getTypes().contains(DescriptionType
.INDIVIDUALS_ASSOCIATION
))
276 .findFirst().orElseGet(()->{
277 TaxonDescription td
= TaxonDescription
.NewInstance(taxon
);
278 td
.addType(DescriptionType
.INDIVIDUALS_ASSOCIATION
);
279 td
.setTitleCache("Specimens for " + taxon
.getTitleCache(), true);
282 IndividualsAssociation association
= IndividualsAssociation
.NewInstance(specimen
);
283 taxonDescription
.addElement(association
);
284 taxonService
.saveOrUpdate(taxon
);
285 result
.addUpdatedObject(taxon
);
287 UUID specimenDescriptionUuid
= wrapper
.getDescription().getDescriptionUuid();
288 DescriptionBaseDto descriptionDto
= wrapper
.getDescription();
289 DescriptionBase
<?
> specimenDescription
= descriptionService
.load(specimenDescriptionUuid
);
290 //if description already exist use the loaded one and add changed data otherwise create a new one and add to specimen
291 if (specimenDescription
== null){
292 specimenDescription
= SpecimenDescription
.NewInstance(specimen
);
293 specimenDescription
.setUuid(specimenDescriptionUuid
);
294 List
<DescriptionElementDto
> elementDtos
= descriptionDto
.getElements();
296 for (DescriptionElementDto elementDto
: elementDtos
){
297 if (elementDto
instanceof CategoricalDataDto
){
298 eu
.etaxonomy
.cdm
.model
.description
.Character feature
= DefinedTermBase
.getTermByClassAndUUID(eu
.etaxonomy
.cdm
.model
.description
.Character
.class, elementDto
.getFeatureUuid());
299 CategoricalData data
= CategoricalData
.NewInstance(feature
);
300 for (StateDataDto stateDto
:((CategoricalDataDto
) elementDto
).getStates()){
301 State state
= DefinedTermBase
.getTermByClassAndUUID(State
.class, stateDto
.getState().getUuid());
302 data
.addStateData(state
);
303 specimenDescription
.addElement(data
);
306 if (elementDto
instanceof QuantitativeDataDto
){
307 eu
.etaxonomy
.cdm
.model
.description
.Character feature
= DefinedTermBase
.getTermByClassAndUUID(eu
.etaxonomy
.cdm
.model
.description
.Character
.class, elementDto
.getFeatureUuid());
308 QuantitativeData data
= QuantitativeData
.NewInstance(feature
);
309 if (((QuantitativeDataDto
) elementDto
).getMeasurementUnit() != null){
310 MeasurementUnit unit
= DefinedTermBase
.getTermByClassAndUUID(MeasurementUnit
.class, ((QuantitativeDataDto
) elementDto
).getMeasurementUnit().getUuid());
314 for (StatisticalMeasurementValueDto stateDto
:((QuantitativeDataDto
) elementDto
).getValues()){
315 StatisticalMeasure statMeasure
= DefinedTermBase
.getTermByClassAndUUID(StatisticalMeasure
.class, stateDto
.getType().getUuid());
316 StatisticalMeasurementValue value
= StatisticalMeasurementValue
.NewInstance(statMeasure
, stateDto
.getValue());
317 data
.addStatisticalValue(value
);
318 specimenDescription
.addElement(data
);
324 List
<DescriptionElementDto
> elementDtos
= descriptionDto
.getElements();
325 for (DescriptionElementDto elementDto
: elementDtos
){
326 if (elementDto
instanceof CategoricalDataDto
){
327 eu
.etaxonomy
.cdm
.model
.description
.Character feature
= DefinedTermBase
.getTermByClassAndUUID(eu
.etaxonomy
.cdm
.model
.description
.Character
.class, elementDto
.getFeatureUuid());
328 List
<DescriptionElementBase
> uniqueElementList
= specimenDescription
.getElements().stream().filter(element
-> element
.getUuid().equals(elementDto
.getElementUuid())).collect(Collectors
.toList());
329 List
<State
> allStates
= new ArrayList
<>();
330 CategoricalData element
= null;
331 if (uniqueElementList
.size() == 1){
332 element
= HibernateProxyHelper
.deproxy(uniqueElementList
.get(0), CategoricalData
.class);
334 element
= CategoricalData
.NewInstance(feature
);
336 for (StateDataDto stateDto
:((CategoricalDataDto
) elementDto
).getStates()){
337 State state
= DefinedTermBase
.getTermByClassAndUUID(State
.class, stateDto
.getState().getUuid());
338 allStates
.add(state
);
340 element
.setStateDataOnly(allStates
);
342 if (elementDto
instanceof QuantitativeDataDto
){
343 eu
.etaxonomy
.cdm
.model
.description
.Character feature
= DefinedTermBase
.getTermByClassAndUUID(eu
.etaxonomy
.cdm
.model
.description
.Character
.class, elementDto
.getFeatureUuid());
344 QuantitativeData data
= QuantitativeData
.NewInstance(feature
);
345 if (((QuantitativeDataDto
) elementDto
).getMeasurementUnit() != null){
346 MeasurementUnit unit
= DefinedTermBase
.getTermByClassAndUUID(MeasurementUnit
.class, ((QuantitativeDataDto
) elementDto
).getMeasurementUnit().getUuid());
350 for (StatisticalMeasurementValueDto stateDto
:((QuantitativeDataDto
) elementDto
).getValues()){
351 StatisticalMeasure statMeasure
= DefinedTermBase
.getTermByClassAndUUID(StatisticalMeasure
.class, stateDto
.getType().getUuid());
352 StatisticalMeasurementValue value
= StatisticalMeasurementValue
.NewInstance(statMeasure
, stateDto
.getValue());
353 data
.addStatisticalValue(value
);
354 specimenDescription
.addElement(data
);
360 //add specimen description to data set
361 specimenDescription
.addDescriptiveDataSet(dataSet
);
362 //add taxon description with IndividualsAssociation to the specimen to data set
363 taxonDescription
.addDescriptiveDataSet(dataSet
);
364 result
.addUpdatedObject(specimen
);
365 result
.addUpdatedObject(specimenDescription
);
366 result
.addUpdatedObject(taxonDescription
);
368 saveOrUpdate(dataSet
);
372 private SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, UUID taxonNodeUuid
,
374 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
);
375 DescriptiveDataSet descriptiveDataSet
= load(datasetUuid
);
376 SpecimenOrObservationBase
<?
> specimen
= HibernateProxyHelper
.deproxy(description
.getDescribedSpecimenOrObservation(), SpecimenOrObservationBase
.class);
377 //supplemental information
379 taxonNode
= findTaxonNodeForDescription(description
, descriptiveDataSet
);
381 FieldUnit fieldUnit
= null;
382 String identifier
= null;
383 NamedArea country
= null;
387 //taxon node was found
390 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
391 Arrays
.asList(new String
[]{
393 "gatheringEvent.country"
395 if(fieldUnits
.size()>1){
396 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
400 if (fieldUnits
.size()>0){
401 fieldUnit
= fieldUnits
.iterator().next();
405 if(HibernateProxyHelper
.isInstanceOf(specimen
, DerivedUnit
.class)){
406 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
409 if(fieldUnit
!= null && fieldUnit
.getGatheringEvent() != null){
410 country
= fieldUnit
.getGatheringEvent().getCountry();
412 //get default taxon description
413 // TaxonDescription defaultTaxonDescription = findDefaultDescription(description.getUuid(), descriptiveDataSet.getUuid());
414 TaxonDescription defaultTaxonDescription
= recurseDefaultDescription(taxonNode
, descriptiveDataSet
);
415 TaxonRowWrapperDTO taxonRowWrapper
= defaultTaxonDescription
!= null
416 ?
createTaxonRowWrapper(defaultTaxonDescription
, descriptiveDataSet
.getUuid()) : null;
417 // use description not specimen for specimenRow
418 SpecimenRowWrapperDTO specimenRowWrapperDTO
= new SpecimenRowWrapperDTO(DescriptionBaseDto
.fromDescription(description
), SpecimenOrObservationDTOFactory
.fromEntity(specimen
), specimen
.getRecordBasis(), new TaxonNodeDto(taxonNode
), fieldUnit
, identifier
, country
);
419 specimenRowWrapperDTO
.setDefaultDescription(taxonRowWrapper
);
420 return specimenRowWrapperDTO
;
424 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, UUID descriptiveDataSetUuid
){
425 return createSpecimenRowWrapper(description
, null, descriptiveDataSetUuid
);
429 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(UUID specimenUuid
, UUID taxonNodeUuid
, UUID descriptiveDataSetUuid
){
431 SpecimenOrObservationBase
<?
> specimen
= occurrenceService
.load(specimenUuid
);
432 SpecimenDescription specimenDescription
= findSpecimenDescription(descriptiveDataSetUuid
, specimen
, true);
433 return createSpecimenRowWrapper(specimenDescription
, taxonNodeUuid
, descriptiveDataSetUuid
);
437 @Transactional(readOnly
= false)
438 public UpdateResult
updateCaches(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
439 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
441 clazz
= DescriptiveDataSet
.class;
443 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
446 private TaxonDescription
findTaxonDescriptionByDescriptionType(DescriptiveDataSet dataSet
, Taxon taxon
, DescriptionType descriptionType
){
447 Optional
<TaxonDescription
> first
= taxon
.getDescriptions().stream()
448 .filter(desc
-> desc
.getTypes().stream().anyMatch(type
-> type
.equals(descriptionType
)))
449 .filter(desc
-> dataSet
.getDescriptions().contains(desc
))
451 if(first
.isPresent()){
452 return HibernateProxyHelper
.deproxy(descriptionService
.load(first
.get().getUuid(),
453 Arrays
.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription
.class);
459 public TaxonDescription
findTaxonDescriptionByDescriptionType(UUID dataSetUuid
, UUID taxonNodeUuid
, DescriptionType descriptionType
){
460 DescriptiveDataSet dataSet
= load(dataSetUuid
);
461 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
);
462 return findTaxonDescriptionByDescriptionType(dataSet
, taxonNode
.getTaxon(), descriptionType
);
466 @Transactional(readOnly
=false)
467 public UpdateResult
generatePolytomousKey(UUID descriptiveDataSetUuid
, UUID taxonUuid
) {
468 UpdateResult result
= new UpdateResult();
470 PolytomousKeyGeneratorConfigurator keyConfig
= new PolytomousKeyGeneratorConfigurator();
471 DescriptiveDataSet descriptiveDataSet
= load(descriptiveDataSetUuid
);
472 keyConfig
.setDataSet(descriptiveDataSet
);
473 PolytomousKey key
= new PolytomousKeyGenerator().invoke(keyConfig
);
474 IdentifiableServiceConfiguratorImpl
<PolytomousKey
> serviceConfig
= new IdentifiableServiceConfiguratorImpl
<>();
475 serviceConfig
.setTitleSearchString(descriptiveDataSet
.getTitleCache());
476 List
<PolytomousKey
> list
= polytomousKeyService
.findByTitle(serviceConfig
).getRecords();
478 list
.forEach(polytomousKey
->polytomousKeyService
.delete(polytomousKey
));
480 key
.setTitleCache(descriptiveDataSet
.getTitleCache(), true);
482 Taxon taxon
= (Taxon
) taxonService
.load(taxonUuid
);
483 key
.addTaxonomicScope(taxon
);
485 polytomousKeyService
.saveOrUpdate(key
);
487 result
.setCdmEntity(key
);
488 result
.addUpdatedObject(taxon
);
493 @Transactional(readOnly
=false)
494 public DeleteResult
removeDescription(UUID descriptionUuid
, UUID descriptiveDataSetUuid
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
495 DeleteResult result
= new DeleteResult();
496 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
497 DescriptionBase
<?
> descriptionBase
= descriptionService
.load(descriptionUuid
);
498 if(dataSet
==null || descriptionBase
==null){
502 removeDescriptionFromDataSet(result
, dataSet
, descriptionBase
, config
);
509 @Transactional(readOnly
=false)
510 public DeleteResult
removeDescriptions(List
<UUID
> descriptionUuids
, UUID descriptiveDataSetUuid
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
511 DeleteResult result
= new DeleteResult();
512 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
513 List
<DescriptionBase
> descriptions
= descriptionService
.load(descriptionUuids
, null);
514 if(dataSet
==null || descriptions
==null){
518 for (DescriptionBase
<?
> description
: descriptions
){
519 removeDescriptionFromDataSet(result
, dataSet
, description
, config
);
525 private void removeDescriptionFromDataSet(DeleteResult result
, DescriptiveDataSet dataSet
,
526 DescriptionBase
<?
> description
, RemoveDescriptionsFromDescriptiveDataSetConfigurator config
) {
527 if (description
== null){
530 boolean success
= dataSet
.removeDescription(description
);
531 result
.addDeletedObject(description
);// remove taxon description with IndividualsAssociation from data set
532 if(description
instanceof SpecimenDescription
){
533 @SuppressWarnings({ "unchecked", "cast" })
534 //NOTE: don't remove cast as it does not compile on some systems
535 Set
<IndividualsAssociation
> associations
= (Set
<IndividualsAssociation
>)dataSet
.getDescriptions()
537 .flatMap(desc
->desc
.getElements().stream())// put all description element in one stream
538 .filter(element
->element
instanceof IndividualsAssociation
)
539 .map(ia
->(IndividualsAssociation
)ia
)
540 .collect(Collectors
.toSet());
542 for (IndividualsAssociation individualsAssociation
: associations
) {
543 if(individualsAssociation
.getAssociatedSpecimenOrObservation().equals(description
.getDescribedSpecimenOrObservation())){
544 dataSet
.removeDescription(individualsAssociation
.getInDescription());
545 result
.addUpdatedObject(individualsAssociation
.getInDescription());
549 if (description
instanceof TaxonDescription
){
550 DeleteResult isDeletable
= descriptionService
.isDeletable(description
.getUuid());
551 for (CdmBase relatedCdmBase
: isDeletable
.getRelatedObjects()){
552 if (relatedCdmBase
instanceof CdmLinkSource
){
553 CdmLinkSource linkSource
= (CdmLinkSource
)relatedCdmBase
;
554 if (linkSource
.getTarget().equals(this)){
562 if (!config
.isOnlyRemoveDescriptionsFromDataSet()){
563 DeleteResult deleteResult
= descriptionService
.deleteDescription(description
);
564 result
.includeResult(deleteResult
);
565 result
.addUpdatedObject(dataSet
);
567 MergeResult
<DescriptiveDataSet
> mergeResult
= dao
.merge(dataSet
, true);
568 result
.addUpdatedObject(mergeResult
.getMergedEntity());
571 result
.setStatus(success?Status
.OK
:Status
.ERROR
);
575 @Transactional(readOnly
= false)
576 public DeleteResult
delete(UUID datasetUuid
, DeleteDescriptiveDataSetConfigurator config
, IProgressMonitor monitor
){
577 DescriptiveDataSet dataSet
= dao
.load(datasetUuid
);
578 monitor
.beginTask("Delete Descriptive Dataset", dataSet
.getDescriptions().size() +1);
580 DeleteResult result
= new DeleteResult();
581 DeleteResult descriptionResult
= new DeleteResult();
582 if (!dataSet
.getDescriptions().isEmpty()){
583 Set
<DescriptionBase
> descriptions
= new HashSet();;
584 for (DescriptionBase desc
: dataSet
.getDescriptions()){
585 descriptions
.add(desc
);
587 monitor
.subTask("Delete descriptions");
588 for (DescriptionBase desc
: descriptions
){
589 dataSet
.removeDescription(desc
);
590 if (desc
instanceof SpecimenDescription
&& config
.isDeleteAllSpecimenDescriptions()){
591 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
592 }else if (desc
instanceof TaxonDescription
){
593 if( desc
.getTypes().contains(DescriptionType
.DEFAULT_VALUES_FOR_AGGREGATION
) && config
.isDeleteAllDefaultDescriptions()){
594 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
595 }else if (desc
.getTypes().contains(DescriptionType
.SECONDARY_DATA
) && config
.isDeleteAllLiteratureDescriptions()){
596 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
597 }else if (desc
.getTypes().contains(DescriptionType
.AGGREGATED_STRUC_DESC
) && config
.isDeleteAllAggregatedDescriptions()){
598 descriptionResult
.includeResult(descriptionService
.deleteDescription(desc
));
610 result
.includeResult(descriptionResult
);
611 result
.setStatus(Status
.OK
);
612 result
.addDeletedObject(dataSet
);
617 @Transactional(readOnly
=false)
618 public TaxonRowWrapperDTO
createTaxonDescription(UUID dataSetUuid
, UUID taxonNodeUuid
, DescriptionType descriptionType
){
619 DescriptiveDataSet dataSet
= load(dataSetUuid
);
620 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
, Arrays
.asList("taxon"));
621 TaxonDescription newTaxonDescription
= TaxonDescription
.NewInstance(taxonNode
.getTaxon());
622 newTaxonDescription
.setTitleCache(dataSet
.getLabel()+": "+newTaxonDescription
.generateTitle(), true); //$NON-NLS-2$
623 newTaxonDescription
.getTypes().add(descriptionType
);
624 dataSet
.addDescription(newTaxonDescription
);
625 saveOrUpdate(dataSet
);
626 return createTaxonRowWrapper(newTaxonDescription
, dataSet
.getUuid());
630 public Map
<UUID
, List
<TermDto
>> getSupportedStatesForFeature(Set
<UUID
> featureUuids
){
631 return termDao
.getSupportedStatesForFeature(featureUuids
);
635 @Transactional(readOnly
=false)
636 public SpecimenDescription
findSpecimenDescription(UUID descriptiveDataSetUuid
, SpecimenOrObservationBase specimen
, boolean addDatasetSource
){
637 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
638 // SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
640 Set
<?
extends Feature
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctTerms();
641 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
643 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
644 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
646 //check if description is already added to data set
647 if(dataSet
.getDescriptions().contains(specimenDescription
) ){
648 return specimenDescription
;
651 //gather specimen description features and check for match with dataset features
652 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
653 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
654 Feature feature
= specimenDescriptionElement
.getFeature();
655 specimenDescriptionFeatures
.add(feature
);
656 if(datasetFeatures
.contains(feature
) && RowWrapperDTO
.hasData(specimenDescriptionElement
)){
657 matchingDescriptionElements
.add(specimenDescriptionElement
);
661 //Create new specimen description if description has not already been added to the dataset
662 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
663 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
665 //check for equals description element (same feature and same values)
666 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
667 for(DescriptionElementBase element
:matchingDescriptionElements
){
668 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
670 list
= new ArrayList
<>();
673 featureToElementMap
.put(element
.getFeature(), list
);
675 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
676 for(Feature feature
:featureToElementMap
.keySet()){
677 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
678 //no duplicate description elements found for this feature
679 if(elements
.size()==1){
680 descriptionElementsToClone
.add(elements
.get(0));
682 //duplicates found -> check if all are equal
684 DescriptionElementBase match
= null;
685 for (DescriptionElementBase descriptionElementBase
: elements
) {
687 match
= descriptionElementBase
;
689 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
691 //TODO: propagate message
692 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
693 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
698 descriptionElementsToClone
.add(match
);
702 //clone matching descriptionElements
703 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
704 DescriptionElementBase clone
;
705 clone
= descriptionElementBase
.clone(newDesription
);
706 clone
.getSources().forEach(source
-> {
707 if(descriptionElementBase
instanceof CategoricalData
){
708 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, Arrays
.asList(new Language
[]{Language
.DEFAULT()}));
709 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
711 else if(descriptionElementBase
instanceof QuantitativeData
){
712 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, Arrays
.asList(new Language
[]{Language
.DEFAULT()}));
713 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
718 //add sources of data set
719 if(addDatasetSource
){
720 dataSet
.getSources().forEach(source
->{
722 newDesription
.addSource(source
.clone());
723 } catch (CloneNotSupportedException e
) {
728 return newDesription
;
732 //TODO: this should either be solved in the model class itself
733 //OR this should cover all possibilities including modifiers for example
734 private class DescriptionElementCompareWrapper
{
736 private DescriptionElementBase element
;
737 private Set
<UUID
> stateUuids
= new HashSet
<>();
738 private Set
<BigDecimal
> avgs
= new HashSet
<>();
739 private Set
<BigDecimal
> exacts
= new HashSet
<>();
740 private Set
<BigDecimal
> maxs
= new HashSet
<>();
741 private Set
<BigDecimal
> mins
= new HashSet
<>();
742 private Set
<BigDecimal
> sampleSizes
= new HashSet
<>();
743 private Set
<BigDecimal
> standardDevs
= new HashSet
<>();
744 private Set
<BigDecimal
> lowerBounds
= new HashSet
<>();
745 private Set
<BigDecimal
> upperBounds
= new HashSet
<>();
746 private Set
<BigDecimal
> variances
= new HashSet
<>();
748 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
749 this.element
= element
;
750 if(element
.isInstanceOf(CategoricalData
.class)){
751 CategoricalData elementData
= (CategoricalData
)element
;
752 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
754 else if(element
.isInstanceOf(QuantitativeData
.class)){
755 QuantitativeData elementData
= (QuantitativeData
)element
;
756 elementData
.getStatisticalValues().forEach(value
->{
757 if(value
.getType().equals(StatisticalMeasure
.AVERAGE())){
758 avgs
.add(value
.getValue());
760 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
761 exacts
.add(value
.getValue());
764 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
765 maxs
.add(value
.getValue());
767 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
768 mins
.add(value
.getValue());
770 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
771 sampleSizes
.add(value
.getValue());
774 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
775 standardDevs
.add(value
.getValue());
777 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
778 lowerBounds
.add(value
.getValue());
781 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
782 upperBounds
.add(value
.getValue());
784 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
785 variances
.add(value
.getValue());
792 public int hashCode() {
793 final int prime
= 31;
795 result
= prime
* result
+ getOuterType().hashCode();
796 result
= prime
* result
+ ((avgs
== null) ?
0 : avgs
.hashCode());
797 result
= prime
* result
+ ((element
== null) ?
0 : element
.hashCode());
798 result
= prime
* result
+ ((exacts
== null) ?
0 : exacts
.hashCode());
799 result
= prime
* result
+ ((lowerBounds
== null) ?
0 : lowerBounds
.hashCode());
800 result
= prime
* result
+ ((maxs
== null) ?
0 : maxs
.hashCode());
801 result
= prime
* result
+ ((mins
== null) ?
0 : mins
.hashCode());
802 result
= prime
* result
+ ((sampleSizes
== null) ?
0 : sampleSizes
.hashCode());
803 result
= prime
* result
+ ((standardDevs
== null) ?
0 : standardDevs
.hashCode());
804 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
805 result
= prime
* result
+ ((upperBounds
== null) ?
0 : upperBounds
.hashCode());
806 result
= prime
* result
+ ((variances
== null) ?
0 : variances
.hashCode());
811 public boolean equals(Object obj
) {
818 if (getClass() != obj
.getClass()) {
821 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
822 if (!getOuterType().equals(other
.getOuterType())) {
826 if (other
.avgs
!= null) {
829 } else if (!avgs
.equals(other
.avgs
)) {
832 if (element
== null) {
833 if (other
.element
!= null) {
836 } else if (!element
.equals(other
.element
)) {
839 if (exacts
== null) {
840 if (other
.exacts
!= null) {
843 } else if (!exacts
.equals(other
.exacts
)) {
846 if (lowerBounds
== null) {
847 if (other
.lowerBounds
!= null) {
850 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
854 if (other
.maxs
!= null) {
857 } else if (!maxs
.equals(other
.maxs
)) {
861 if (other
.mins
!= null) {
864 } else if (!mins
.equals(other
.mins
)) {
867 if (sampleSizes
== null) {
868 if (other
.sampleSizes
!= null) {
871 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
874 if (standardDevs
== null) {
875 if (other
.standardDevs
!= null) {
878 } else if (!standardDevs
.equals(other
.standardDevs
)) {
881 if (stateUuids
== null) {
882 if (other
.stateUuids
!= null) {
885 } else if (!stateUuids
.equals(other
.stateUuids
)) {
888 if (upperBounds
== null) {
889 if (other
.upperBounds
!= null) {
892 } else if (!upperBounds
.equals(other
.upperBounds
)) {
895 if (variances
== null) {
896 if (other
.variances
!= null) {
899 } else if (!variances
.equals(other
.variances
)) {
905 private DescriptiveDataSetService
getOuterType() {
906 return DescriptiveDataSetService
.this;
911 public DescriptiveDataSetBaseDto
getDescriptiveDataSetDtoByUuid(UUID uuid
) {
912 return dao
.getDescriptiveDataSetDtoByUuid(uuid
);