1 package eu
.etaxonomy
.cdm
.api
.service
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Arrays
;
5 import java
.util
.Collection
;
6 import java
.util
.HashMap
;
7 import java
.util
.HashSet
;
10 import java
.util
.Optional
;
12 import java
.util
.UUID
;
14 import org
.apache
.log4j
.Logger
;
15 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
16 import org
.springframework
.stereotype
.Service
;
17 import org
.springframework
.transaction
.annotation
.Transactional
;
19 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
.Status
;
20 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
21 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenRowWrapperDTO
;
22 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TaxonRowWrapperDTO
;
23 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
24 import eu
.etaxonomy
.cdm
.common
.monitor
.IRemotingProgressMonitor
;
25 import eu
.etaxonomy
.cdm
.filter
.TaxonNodeFilter
;
26 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
27 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
28 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
29 import eu
.etaxonomy
.cdm
.model
.common
.Marker
;
30 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
31 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
32 import eu
.etaxonomy
.cdm
.model
.description
.Character
;
33 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
34 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
35 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
36 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveSystemRole
;
37 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
38 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
39 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
40 import eu
.etaxonomy
.cdm
.model
.description
.StateData
;
41 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
42 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasurementValue
;
43 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
44 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
45 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
46 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
47 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
48 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
49 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceType
;
50 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
51 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
52 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
53 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
54 import eu
.etaxonomy
.cdm
.persistence
.dao
.description
.IDescriptiveDataSetDao
;
55 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
56 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
57 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
58 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
61 @Transactional(readOnly
=true)
62 public class DescriptiveDataSetService
63 extends IdentifiableServiceBase
<DescriptiveDataSet
, IDescriptiveDataSetDao
>
64 implements IDescriptiveDataSetService
{
66 private static Logger logger
= Logger
.getLogger(DescriptiveDataSetService
.class);
69 private IOccurrenceService occurrenceService
;
72 private ITaxonService taxonService
;
75 private IDescriptionService descriptionService
;
78 private ITaxonNodeService taxonNodeService
;
81 private IProgressMonitorService progressMonitorService
;
83 //FIXME: Use actual MarkerType for default descriptions when implemented #7957
84 private MarkerType MARKER_DEFAULT
= MarkerType
.TO_BE_CHECKED();
88 protected void setDao(IDescriptiveDataSetDao dao
) {
93 public Map
<DescriptionBase
, Set
<DescriptionElementBase
>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet
, Set
<Feature
> features
, Integer pageSize
, Integer pageNumber
,
94 List
<String
> propertyPaths
) {
95 return dao
.getDescriptionElements(descriptiveDataSet
, features
, pageSize
, pageNumber
, propertyPaths
);
99 public <T
extends DescriptionElementBase
> Map
<UuidAndTitleCache
, Map
<UUID
, Set
<T
>>> getTaxonFeatureDescriptionElementMap(
100 Class
<T
> clazz
, UUID descriptiveDataSetUuid
, DescriptiveSystemRole role
) {
101 return dao
.getTaxonFeatureDescriptionElementMap(clazz
, descriptiveDataSetUuid
, role
);
105 public List
<UuidAndTitleCache
<DescriptiveDataSet
>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements
, String pattern
) {
106 return dao
.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements
, pattern
);
110 public ArrayList
<RowWrapperDTO
> getRowWrapper(DescriptiveDataSet descriptiveDataSet
, IProgressMonitor monitor
) {
111 monitor
.beginTask("Load row wrapper", descriptiveDataSet
.getDescriptions().size());
112 ArrayList
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
113 Set
<DescriptionBase
> descriptions
= descriptiveDataSet
.getDescriptions();
114 for (DescriptionBase description
: descriptions
) {
115 if(monitor
.isCanceled()){
116 return new ArrayList
<>();
118 RowWrapperDTO rowWrapper
= null;
119 if(HibernateProxyHelper
.isInstanceOf(description
, TaxonDescription
.class)){
120 rowWrapper
= createTaxonRowWrapper(description
.getUuid(), descriptiveDataSet
.getUuid());
122 else if (HibernateProxyHelper
.isInstanceOf(description
, SpecimenDescription
.class)){
123 rowWrapper
= createSpecimenRowWrapper(HibernateProxyHelper
.deproxy(description
, SpecimenDescription
.class), descriptiveDataSet
);
125 if(rowWrapper
!=null){
126 wrappers
.add(rowWrapper
);
134 public Collection
<SpecimenNodeWrapper
> loadSpecimens(DescriptiveDataSet descriptiveDataSet
){
135 List
<UUID
> filteredNodes
= findFilteredTaxonNodes(descriptiveDataSet
);
136 return occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes
, null, null);
140 public List
<UUID
> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
){
141 TaxonNodeFilter filter
= TaxonNodeFilter
.NewRankInstance(descriptiveDataSet
.getMinRank(), descriptiveDataSet
.getMaxRank());
142 descriptiveDataSet
.getGeoFilter().forEach(area
-> filter
.orArea(area
.getUuid()));
143 descriptiveDataSet
.getTaxonSubtreeFilter().forEach(node
-> filter
.orSubtree(node
));
144 filter
.setIncludeUnpublished(true);
146 return taxonNodeService
.uuidList(filter
);
150 public List
<TaxonNode
> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet
, List
<String
> propertyPaths
){
151 return taxonNodeService
.load(findFilteredTaxonNodes(descriptiveDataSet
), propertyPaths
);
154 private TaxonNode
findTaxonNodeForDescription(TaxonNode taxonNode
, SpecimenOrObservationBase specimen
){
155 Collection
<SpecimenNodeWrapper
> nodeWrapper
= occurrenceService
.listUuidAndTitleCacheByAssociatedTaxon(Arrays
.asList(taxonNode
.getUuid()), null, null);
156 for (SpecimenNodeWrapper specimenNodeWrapper
: nodeWrapper
) {
157 if(specimenNodeWrapper
.getUuidAndTitleCache().getId().equals(specimen
.getId())){
165 public TaxonRowWrapperDTO
createTaxonRowWrapper(UUID taxonDescriptionUuid
, UUID descriptiveDataSetUuid
) {
166 TaxonNode taxonNode
= null;
167 Classification classification
= null;
168 TaxonDescription description
= (TaxonDescription
) descriptionService
.load(taxonDescriptionUuid
,
169 Arrays
.asList("taxon", "descriptionElements", "descriptionElements.feature"));
170 DescriptiveDataSet descriptiveDataSet
= dao
.load(descriptiveDataSetUuid
, null);
171 Optional
<TaxonNode
> first
= descriptiveDataSet
.getTaxonSubtreeFilter().stream()
172 .filter(node
->node
.getClassification()!=null).findFirst();
173 Optional
<Classification
> classificationOptional
= first
.map(node
->node
.getClassification());
174 if(classificationOptional
.isPresent()){
175 classification
= classificationOptional
.get();
176 Taxon taxon
= (Taxon
) taxonService
.load(description
.getTaxon().getId(), Arrays
.asList("taxonNodes", "taxonNodes.classification"));
177 taxonNode
= taxon
.getTaxonNode(classification
);
179 return new TaxonRowWrapperDTO(description
, new TaxonNodeDto(taxonNode
));
183 public SpecimenRowWrapperDTO
createSpecimenRowWrapper(SpecimenDescription description
, DescriptiveDataSet descriptiveDataSet
){
184 SpecimenOrObservationBase specimen
= description
.getDescribedSpecimenOrObservation();
185 TaxonNode taxonNode
= null;
186 FieldUnit fieldUnit
= null;
187 String identifier
= null;
188 NamedArea country
= null;
189 //supplemental information
191 Set
<TaxonNode
> taxonSubtreeFilter
= descriptiveDataSet
.getTaxonSubtreeFilter();
192 for (TaxonNode node
: taxonSubtreeFilter
) {
194 List
<String
> taxonNodePropertyPath
= Arrays
.asList("taxon", "taxon.descriptions", "taxon.descriptions.markers");
195 node
= taxonNodeService
.load(node
.getId(), taxonNodePropertyPath
);
196 taxonNode
= findTaxonNodeForDescription(node
, specimen
);
201 //check for child nodes
202 List
<TaxonNode
> allChildren
= taxonNodeService
.loadChildNodesOfTaxonNode(node
, taxonNodePropertyPath
, true, true, null);
203 for (TaxonNode child
: allChildren
) {
204 taxonNode
= findTaxonNodeForDescription(child
, specimen
);
214 //taxon node was found
217 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(specimen
.getUuid(),
218 Arrays
.asList(new String
[]{
220 "gatheringEvent.country"
222 if(fieldUnits
.size()!=1){
223 logger
.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
227 fieldUnit
= fieldUnits
.iterator().next();
230 if(HibernateProxyHelper
.isInstanceOf(specimen
, DerivedUnit
.class)){
231 identifier
= occurrenceService
.getMostSignificantIdentifier(HibernateProxyHelper
.deproxy(specimen
, DerivedUnit
.class));
234 if(fieldUnit
!=null && fieldUnit
.getGatheringEvent()!=null){
235 country
= fieldUnit
.getGatheringEvent().getCountry();
237 //get default taxon description
238 TaxonDescription defaultTaxonDescription
= findTaxonDescriptionByMarkerType(descriptiveDataSet
.getUuid(),
239 taxonNode
.getUuid(), MARKER_DEFAULT
);
240 TaxonRowWrapperDTO taxonRowWrapper
= defaultTaxonDescription
!= null
241 ?
createTaxonRowWrapper(defaultTaxonDescription
.getUuid(), descriptiveDataSet
.getUuid()) : null;
242 return new SpecimenRowWrapperDTO(description
, new TaxonNodeDto(taxonNode
), fieldUnit
, identifier
, country
);
246 @Transactional(readOnly
= false)
247 public UpdateResult
updateCaches(Class
<?
extends DescriptiveDataSet
> clazz
, Integer stepSize
,
248 IIdentifiableEntityCacheStrategy
<DescriptiveDataSet
> cacheStrategy
, IProgressMonitor monitor
) {
250 clazz
= DescriptiveDataSet
.class;
252 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
255 private TaxonDescription
findTaxonDescriptionByMarkerType(DescriptiveDataSet dataSet
, Taxon taxon
, MarkerType markerType
){
256 Set
<DescriptionBase
> dataSetDescriptions
= dataSet
.getDescriptions();
257 //filter by DEFAULT descriptions
258 Optional
<TaxonDescription
> first
= taxon
.getDescriptions().stream()
259 .filter(desc
-> desc
.getMarkers().stream().anyMatch(marker
-> marker
.getMarkerType().equals(markerType
)))
260 .filter(defaultDescription
->dataSetDescriptions
.contains(defaultDescription
))
262 if(first
.isPresent()){
263 return HibernateProxyHelper
.deproxy(descriptionService
.load(first
.get().getUuid(),
264 Arrays
.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription
.class);
270 public TaxonDescription
findTaxonDescriptionByMarkerType(UUID dataSetUuid
, UUID taxonNodeUuid
, MarkerType markerType
){
271 DescriptiveDataSet dataSet
= load(dataSetUuid
);
272 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
);
273 return findTaxonDescriptionByMarkerType(dataSet
, taxonNode
.getTaxon(), markerType
);
277 @Transactional(readOnly
=false)
278 public UpdateResult
aggregateTaxonDescription(UUID taxonNodeUuid
, UUID descriptiveDataSetUuid
,
279 IRemotingProgressMonitor monitor
){
280 UpdateResult result
= new UpdateResult();
282 TaxonNode node
= taxonNodeService
.load(taxonNodeUuid
);
283 Taxon taxon
= HibernateProxyHelper
.deproxy(taxonService
.load(node
.getTaxon().getUuid()), Taxon
.class);
284 result
.setCdmEntity(taxon
);
286 //get all "computed" descriptions from all sub nodes
287 List
<TaxonNode
> childNodes
= taxonNodeService
.listChildrenOf(node
, null, null, true, false, null);
288 List
<TaxonDescription
> computedDescriptions
= new ArrayList
<>();
290 childNodes
.stream().map(childNode
-> childNode
.getTaxon())
291 .forEach(childTaxon
-> childTaxon
.getDescriptions().stream()
292 // filter out non-computed descriptions
293 .filter(description
-> description
.getMarkers().stream()
294 .anyMatch(marker
-> marker
.getMarkerType().equals(MarkerType
.COMPUTED())))
295 // add them to the list
296 .forEach(computedDescription
-> computedDescriptions
.add(computedDescription
)));
298 UpdateResult aggregateDescription
= aggregateDescription(taxon
, computedDescriptions
,
299 "[Taxon Descriptions]"+taxon
.getTitleCache(), descriptiveDataSetUuid
);
300 result
.includeResult(aggregateDescription
);
301 result
.setCdmEntity(aggregateDescription
.getCdmEntity());
302 aggregateDescription
.setCdmEntity(null);
307 @Transactional(readOnly
=false)
308 public UpdateResult
aggregateDescription(UUID taxonUuid
, List
<UUID
> descriptionUuids
, String descriptionTitle
309 , UUID descriptiveDataSetUuid
) {
310 UpdateResult result
= new UpdateResult();
312 TaxonBase taxonBase
= taxonService
.load(taxonUuid
);
313 if(!(taxonBase
instanceof Taxon
)){
314 result
.addException(new ClassCastException("The given taxonUUID does not belong to a taxon"));
318 Taxon taxon
= (Taxon
)taxonBase
;
320 List
<DescriptionBase
> descriptions
= descriptionService
.load(descriptionUuids
, null);
322 UpdateResult aggregateDescriptionResult
= aggregateDescription(taxon
, descriptions
, descriptionTitle
, descriptiveDataSetUuid
);
323 result
.setCdmEntity(aggregateDescriptionResult
.getCdmEntity());
324 aggregateDescriptionResult
.setCdmEntity(null);
325 result
.includeResult(aggregateDescriptionResult
);
329 @SuppressWarnings("unchecked")
330 private UpdateResult
aggregateDescription(Taxon taxon
, List
<?
extends DescriptionBase
> descriptions
, String descriptionTitle
331 , UUID descriptiveDataSetUuid
) {
332 UpdateResult result
= new UpdateResult();
333 Map
<Character
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
335 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
337 result
.addException(new IllegalArgumentException("Could not find data set for uuid "+descriptiveDataSetUuid
));
342 //extract all character description elements
343 descriptions
.forEach(description
->{
344 description
.getElements()
346 //filter out elements that do not have a Characters as Feature
347 .filter(element
->HibernateProxyHelper
.isInstanceOf(((DescriptionElementBase
)element
).getFeature(), Character
.class))
349 DescriptionElementBase descriptionElement
= (DescriptionElementBase
)ele
;
350 List
<DescriptionElementBase
> list
= featureToElementMap
.get(descriptionElement
.getFeature());
352 list
= new ArrayList
<>();
354 list
.add(descriptionElement
);
355 featureToElementMap
.put(HibernateProxyHelper
.deproxy(descriptionElement
.getFeature(), Character
.class), list
);
359 // delete existing aggregation description, if present
360 TaxonDescription aggregation
= findTaxonDescriptionByMarkerType(dataSet
, taxon
, MarkerType
.COMPUTED());
361 if(aggregation
!=null){
362 removeDescription(aggregation
.getUuid(), descriptiveDataSetUuid
);
365 // create new aggregation
366 TaxonDescription description
= TaxonDescription
.NewInstance(taxon
);
367 description
.setTitleCache("[Aggregation] "+descriptionTitle
, true);
368 description
.addMarker(Marker
.NewInstance(MarkerType
.COMPUTED(), true));
369 IdentifiableSource source
= IdentifiableSource
.NewInstance(OriginalSourceType
.Aggregation
);
370 description
.addSource(source
);
371 description
.addDescriptiveDataSet(dataSet
);
373 featureToElementMap
.forEach((feature
, elements
)->{
374 //aggregate categorical data
375 if(feature
.isSupportsCategoricalData()){
376 CategoricalData aggregate
= CategoricalData
.NewInstance(feature
);
378 .filter(element
->element
instanceof CategoricalData
)
379 .forEach(categoricalData
->((CategoricalData
)categoricalData
).getStateData()
380 .forEach(stateData
->aggregate
.addStateData((StateData
) stateData
.clone())));
381 description
.addElement(aggregate
);
383 //aggregate quantitative data
384 else if(feature
.isSupportsQuantitativeData()){
385 QuantitativeData aggregate
= QuantitativeData
.NewInstance(feature
);
387 .filter(element
->element
instanceof QuantitativeData
)
388 .forEach(categoricalData
->((QuantitativeData
)categoricalData
).getStatisticalValues()
389 .forEach(statisticalValue
->aggregate
.addStatisticalValue((StatisticalMeasurementValue
) statisticalValue
.clone())));
390 description
.addElement(aggregate
);
393 result
.addUpdatedObject(taxon
);
394 result
.setCdmEntity(description
);
399 @Transactional(readOnly
=false)
400 public DeleteResult
removeDescription(UUID descriptionUuid
, UUID descriptiveDataSetUuid
) {
401 DeleteResult result
= new DeleteResult();
402 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
403 DescriptionBase descriptionBase
= descriptionService
.load(descriptionUuid
);
404 if(dataSet
==null || descriptionBase
==null){
408 boolean success
= dataSet
.removeDescription(descriptionBase
);
409 result
.addDeletedObject(descriptionBase
);
410 result
.addUpdatedObject(dataSet
);
411 result
.setStatus(success?Status
.OK
:Status
.ERROR
);
417 @Transactional(readOnly
=false)
418 public TaxonRowWrapperDTO
createTaxonDescription(UUID dataSetUuid
, UUID taxonNodeUuid
, MarkerType markerType
, boolean markerFlag
){
419 DescriptiveDataSet dataSet
= load(dataSetUuid
);
420 TaxonNode taxonNode
= taxonNodeService
.load(taxonNodeUuid
, Arrays
.asList("taxon"));
421 TaxonDescription newTaxonDescription
= TaxonDescription
.NewInstance(taxonNode
.getTaxon());
424 //FIXME: Add specific MarkerTypes to enum (see #7957)
425 if(markerType
.equals(MARKER_DEFAULT
)){
428 else if(markerType
.equals(MarkerType
.IN_BIBLIOGRAPHY())){
429 tag
= "[Literature]";
432 newTaxonDescription
.setTitleCache(tag
+" "+dataSet
.getLabel()+": "+newTaxonDescription
.generateTitle(), true); //$NON-NLS-2$
433 if(markerType
!=null){
434 newTaxonDescription
.addMarker(Marker
.NewInstance(markerType
, markerFlag
));
436 dataSet
.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature
->{
437 if(wsFeature
.isSupportsCategoricalData()){
438 newTaxonDescription
.addElement(CategoricalData
.NewInstance(wsFeature
));
440 else if(wsFeature
.isSupportsQuantitativeData()){
441 newTaxonDescription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
444 dataSet
.addDescription(newTaxonDescription
);
446 return createTaxonRowWrapper(newTaxonDescription
.getUuid(), dataSet
.getUuid());
450 @Transactional(readOnly
=false)
451 public SpecimenDescription
findSpecimenDescription(UUID descriptiveDataSetUuid
, UUID specimenUuid
, boolean addDatasetSource
){
452 DescriptiveDataSet dataSet
= load(descriptiveDataSetUuid
);
453 SpecimenOrObservationBase specimen
= occurrenceService
.load(specimenUuid
);
455 Set
<Character
> datasetFeatures
= dataSet
.getDescriptiveSystem().getDistinctFeatures();
456 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
458 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
459 specimenDescription
= (SpecimenDescription
) descriptionService
.load(specimenDescription
.getUuid());
461 //check if description is already added to data set
462 if(dataSet
.getDescriptions().contains(specimenDescription
)){
463 return specimenDescription
;
466 //gather specimen description features and check for match with dataset features
467 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
468 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
469 Feature feature
= specimenDescriptionElement
.getFeature();
470 specimenDescriptionFeatures
.add(feature
);
471 if(datasetFeatures
.contains(feature
)){
472 matchingDescriptionElements
.add(specimenDescriptionElement
);
476 //Create new specimen description if description has not already been added to the dataset
477 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
478 newDesription
.setTitleCache("Dataset "+dataSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
480 //check for equals description element (same feature and same values)
481 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
482 for(DescriptionElementBase element
:matchingDescriptionElements
){
483 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
485 list
= new ArrayList
<>();
488 featureToElementMap
.put(element
.getFeature(), list
);
490 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
491 for(Feature feature
:featureToElementMap
.keySet()){
492 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
493 //no duplicate description elements found for this feature
494 if(elements
.size()==1){
495 descriptionElementsToClone
.add(elements
.get(0));
497 //duplicates found -> check if all are equal
499 DescriptionElementBase match
= null;
500 for (DescriptionElementBase descriptionElementBase
: elements
) {
502 match
= descriptionElementBase
;
504 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
506 //TODO: propagate message
507 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
508 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
513 descriptionElementsToClone
.add(match
);
517 //clone matching descriptionElements
518 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
519 DescriptionElementBase clone
;
521 clone
= descriptionElementBase
.clone(newDesription
);
522 clone
.getSources().forEach(source
-> {
523 if(descriptionElementBase
instanceof CategoricalData
){
524 TextData label
= new DefaultCategoricalDescriptionBuilder().build((CategoricalData
) descriptionElementBase
, null);
525 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
527 else if(descriptionElementBase
instanceof QuantitativeData
){
528 TextData label
= new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData
) descriptionElementBase
, null);
529 source
.setOriginalNameString(label
.getText(Language
.DEFAULT()));
532 } catch (CloneNotSupportedException e
) {
537 //add all remaining description elements to the new description
538 for(Feature wsFeature
:datasetFeatures
){
539 boolean featureFound
= false;
540 for(DescriptionElementBase element
:newDesription
.getElements()){
541 if(element
.getFeature().equals(wsFeature
)){
547 if(wsFeature
.isSupportsCategoricalData()){
548 newDesription
.addElement(CategoricalData
.NewInstance(wsFeature
));
550 else if(wsFeature
.isSupportsQuantitativeData()){
551 newDesription
.addElement(QuantitativeData
.NewInstance(wsFeature
));
555 //add sources of data set
556 if(addDatasetSource
){
557 dataSet
.getSources().forEach(source
->{
559 newDesription
.addSource((IdentifiableSource
) source
.clone());
560 } catch (CloneNotSupportedException e
) {
565 return newDesription
;
569 //TODO: this should either be solved in the model class itself
570 //OR this should cover all possibilities including modifiers for example
571 private class DescriptionElementCompareWrapper
{
573 private DescriptionElementBase element
;
574 private Set
<UUID
> stateUuids
= new HashSet
<>();
575 private Set
<Float
> avgs
= new HashSet
<>();
576 private Set
<Float
> exacts
= new HashSet
<>();
577 private Set
<Float
> maxs
= new HashSet
<>();
578 private Set
<Float
> mins
= new HashSet
<>();
579 private Set
<Float
> sampleSizes
= new HashSet
<>();
580 private Set
<Float
> standardDevs
= new HashSet
<>();
581 private Set
<Float
> lowerBounds
= new HashSet
<>();
582 private Set
<Float
> upperBounds
= new HashSet
<>();
583 private Set
<Float
> variances
= new HashSet
<>();
585 public DescriptionElementCompareWrapper(DescriptionElementBase element
) {
586 this.element
= element
;
587 if(element
.isInstanceOf(CategoricalData
.class)){
588 CategoricalData elementData
= (CategoricalData
)element
;
589 elementData
.getStatesOnly().forEach(state
->stateUuids
.add(state
.getUuid()));
591 else if(element
.isInstanceOf(QuantitativeData
.class)){
592 QuantitativeData elementData
= (QuantitativeData
)element
;
593 elementData
.getStatisticalValues().forEach(value
->{
594 if(value
.getType().equals(StatisticalMeasure
.AVERAGE())){
595 avgs
.add(value
.getValue());
597 else if(value
.getType().equals(StatisticalMeasure
.EXACT_VALUE())){
598 exacts
.add(value
.getValue());
601 else if(value
.getType().equals(StatisticalMeasure
.MAX())){
602 maxs
.add(value
.getValue());
604 else if(value
.getType().equals(StatisticalMeasure
.MIN())){
605 mins
.add(value
.getValue());
607 else if(value
.getType().equals(StatisticalMeasure
.SAMPLE_SIZE())){
608 sampleSizes
.add(value
.getValue());
611 else if(value
.getType().equals(StatisticalMeasure
.STANDARD_DEVIATION())){
612 standardDevs
.add(value
.getValue());
614 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_LOWER_BOUNDARY())){
615 lowerBounds
.add(value
.getValue());
618 else if(value
.getType().equals(StatisticalMeasure
.TYPICAL_UPPER_BOUNDARY())){
619 upperBounds
.add(value
.getValue());
621 else if(value
.getType().equals(StatisticalMeasure
.VARIANCE())){
622 variances
.add(value
.getValue());
629 public int hashCode() {
630 final int prime
= 31;
632 result
= prime
* result
+ getOuterType().hashCode();
633 result
= prime
* result
+ ((avgs
== null) ?
0 : avgs
.hashCode());
634 result
= prime
* result
+ ((element
== null) ?
0 : element
.hashCode());
635 result
= prime
* result
+ ((exacts
== null) ?
0 : exacts
.hashCode());
636 result
= prime
* result
+ ((lowerBounds
== null) ?
0 : lowerBounds
.hashCode());
637 result
= prime
* result
+ ((maxs
== null) ?
0 : maxs
.hashCode());
638 result
= prime
* result
+ ((mins
== null) ?
0 : mins
.hashCode());
639 result
= prime
* result
+ ((sampleSizes
== null) ?
0 : sampleSizes
.hashCode());
640 result
= prime
* result
+ ((standardDevs
== null) ?
0 : standardDevs
.hashCode());
641 result
= prime
* result
+ ((stateUuids
== null) ?
0 : stateUuids
.hashCode());
642 result
= prime
* result
+ ((upperBounds
== null) ?
0 : upperBounds
.hashCode());
643 result
= prime
* result
+ ((variances
== null) ?
0 : variances
.hashCode());
648 public boolean equals(Object obj
) {
655 if (getClass() != obj
.getClass()) {
658 DescriptionElementCompareWrapper other
= (DescriptionElementCompareWrapper
) obj
;
659 if (!getOuterType().equals(other
.getOuterType())) {
663 if (other
.avgs
!= null) {
666 } else if (!avgs
.equals(other
.avgs
)) {
669 if (element
== null) {
670 if (other
.element
!= null) {
673 } else if (!element
.equals(other
.element
)) {
676 if (exacts
== null) {
677 if (other
.exacts
!= null) {
680 } else if (!exacts
.equals(other
.exacts
)) {
683 if (lowerBounds
== null) {
684 if (other
.lowerBounds
!= null) {
687 } else if (!lowerBounds
.equals(other
.lowerBounds
)) {
691 if (other
.maxs
!= null) {
694 } else if (!maxs
.equals(other
.maxs
)) {
698 if (other
.mins
!= null) {
701 } else if (!mins
.equals(other
.mins
)) {
704 if (sampleSizes
== null) {
705 if (other
.sampleSizes
!= null) {
708 } else if (!sampleSizes
.equals(other
.sampleSizes
)) {
711 if (standardDevs
== null) {
712 if (other
.standardDevs
!= null) {
715 } else if (!standardDevs
.equals(other
.standardDevs
)) {
718 if (stateUuids
== null) {
719 if (other
.stateUuids
!= null) {
722 } else if (!stateUuids
.equals(other
.stateUuids
)) {
725 if (upperBounds
== null) {
726 if (other
.upperBounds
!= null) {
729 } else if (!upperBounds
.equals(other
.upperBounds
)) {
732 if (variances
== null) {
733 if (other
.variances
!= null) {
736 } else if (!variances
.equals(other
.variances
)) {
742 private DescriptiveDataSetService
getOuterType() {
743 return DescriptiveDataSetService
.this;