Merge branch 'release/5.4.0'
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / DescriptiveDataSetService.java
1 package eu.etaxonomy.cdm.api.service;
2
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;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Optional;
11 import java.util.Set;
12 import java.util.UUID;
13 import java.util.stream.Collectors;
14
15 import org.apache.log4j.Logger;
16 import org.springframework.beans.factory.annotation.Autowired;
17 import org.springframework.stereotype.Service;
18 import org.springframework.transaction.annotation.Transactional;
19
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.common.OriginalSourceType;
32 import eu.etaxonomy.cdm.model.description.CategoricalData;
33 import eu.etaxonomy.cdm.model.description.Character;
34 import eu.etaxonomy.cdm.model.description.DescriptionBase;
35 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
36 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
37 import eu.etaxonomy.cdm.model.description.DescriptiveSystemRole;
38 import eu.etaxonomy.cdm.model.description.Feature;
39 import eu.etaxonomy.cdm.model.description.QuantitativeData;
40 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
41 import eu.etaxonomy.cdm.model.description.StateData;
42 import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
43 import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
44 import eu.etaxonomy.cdm.model.description.TaxonDescription;
45 import eu.etaxonomy.cdm.model.description.TextData;
46 import eu.etaxonomy.cdm.model.location.NamedArea;
47 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
48 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
49 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
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.UuidAndTitleCache;
57 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
58
59 @Service
60 @Transactional(readOnly=true)
61 public class DescriptiveDataSetService
62 extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
63 implements IDescriptiveDataSetService {
64
65 private static Logger logger = Logger.getLogger(DescriptiveDataSetService.class);
66
67 @Autowired
68 private IOccurrenceService occurrenceService;
69
70 @Autowired
71 private ITaxonService taxonService;
72
73 @Autowired
74 private IDescriptionService descriptionService;
75
76 @Autowired
77 private ITaxonNodeService taxonNodeService;
78
79 @Autowired
80 private IProgressMonitorService progressMonitorService;
81
82 @Override
83 @Autowired
84 protected void setDao(IDescriptiveDataSetDao dao) {
85 this.dao = dao;
86 }
87
88 @Override
89 public Map<DescriptionBase, Set<DescriptionElementBase>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet, Set<Feature> features, Integer pageSize, Integer pageNumber,
90 List<String> propertyPaths) {
91 return dao.getDescriptionElements(descriptiveDataSet, features, pageSize, pageNumber, propertyPaths);
92 }
93
94 @Override
95 public <T extends DescriptionElementBase> Map<UuidAndTitleCache, Map<UUID, Set<T>>> getTaxonFeatureDescriptionElementMap(
96 Class<T> clazz, UUID descriptiveDataSetUuid, DescriptiveSystemRole role) {
97 return dao.getTaxonFeatureDescriptionElementMap(clazz, descriptiveDataSetUuid, role);
98 }
99
100 @Override
101 public List<UuidAndTitleCache<DescriptiveDataSet>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements, String pattern) {
102 return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
103 }
104
105 @Override
106 public ArrayList<RowWrapperDTO> getRowWrapper(DescriptiveDataSet descriptiveDataSet, IProgressMonitor monitor) {
107 monitor.beginTask("Load row wrapper", descriptiveDataSet.getDescriptions().size());
108 ArrayList<RowWrapperDTO> wrappers = new ArrayList<>();
109 Set<DescriptionBase> descriptions = descriptiveDataSet.getDescriptions();
110 for (DescriptionBase description : descriptions) {
111 if(monitor.isCanceled()){
112 return new ArrayList<>();
113 }
114 RowWrapperDTO rowWrapper = null;
115 if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class)){
116 rowWrapper = createTaxonRowWrapper(description.getUuid(), descriptiveDataSet.getUuid());
117 }
118 else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)){
119 rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSet);
120 }
121 if(rowWrapper!=null){
122 wrappers.add(rowWrapper);
123 }
124 monitor.worked(1);
125 }
126 return wrappers;
127 }
128
129 @Override
130 public Collection<SpecimenNodeWrapper> loadSpecimens(DescriptiveDataSet descriptiveDataSet){
131 List<UUID> filteredNodes = findFilteredTaxonNodes(descriptiveDataSet);
132 return occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
133 }
134
135 @Override
136 public List<UUID> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet){
137 TaxonNodeFilter filter = TaxonNodeFilter.NewRankInstance(descriptiveDataSet.getMinRank(), descriptiveDataSet.getMaxRank());
138 descriptiveDataSet.getGeoFilter().forEach(area -> filter.orArea(area.getUuid()));
139 descriptiveDataSet.getTaxonSubtreeFilter().forEach(node -> filter.orSubtree(node));
140 filter.setIncludeUnpublished(true);
141
142 return taxonNodeService.uuidList(filter);
143 }
144
145 @Override
146 public List<TaxonNode> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet, List<String> propertyPaths){
147 return taxonNodeService.load(findFilteredTaxonNodes(descriptiveDataSet), propertyPaths);
148 }
149
150 private TaxonNode findTaxonNodeForDescription(TaxonNode taxonNode, SpecimenOrObservationBase specimen){
151 Collection<SpecimenNodeWrapper> nodeWrapper = occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(Arrays.asList(taxonNode.getUuid()), null, null);
152 for (SpecimenNodeWrapper specimenNodeWrapper : nodeWrapper) {
153 if(specimenNodeWrapper.getUuidAndTitleCache().getId().equals(specimen.getId())){
154 return taxonNode;
155 }
156 }
157 return null;
158 }
159
160 @Override
161 public TaxonRowWrapperDTO createTaxonRowWrapper(UUID taxonDescriptionUuid, UUID descriptiveDataSetUuid) {
162 TaxonNode taxonNode = null;
163 Classification classification = null;
164 TaxonDescription description = (TaxonDescription) descriptionService.load(taxonDescriptionUuid,
165 Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature"));
166 DescriptiveDataSet descriptiveDataSet = dao.load(descriptiveDataSetUuid, null);
167 Optional<TaxonNode> first = descriptiveDataSet.getTaxonSubtreeFilter().stream()
168 .filter(node->node.getClassification()!=null).findFirst();
169 Optional<Classification> classificationOptional = first.map(node->node.getClassification());
170 if(classificationOptional.isPresent()){
171 classification = classificationOptional.get();
172 Taxon taxon = (Taxon) taxonService.load(description.getTaxon().getId(), Arrays.asList("taxonNodes", "taxonNodes.classification"));
173 taxonNode = taxon.getTaxonNode(classification);
174 }
175 return new TaxonRowWrapperDTO(description, taxonNode);
176 }
177
178 @Override
179 public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, DescriptiveDataSet descriptiveDataSet){
180 SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
181 TaxonNode taxonNode = null;
182 FieldUnit fieldUnit = null;
183 String identifier = null;
184 NamedArea country = null;
185 //supplemental information
186 //get taxon node
187 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
188 for (TaxonNode node : taxonSubtreeFilter) {
189 //check for node
190 List<String> taxonNodePropertyPath = Arrays.asList("taxon", "taxon.descriptions", "taxon.descriptions.markers");
191 node = taxonNodeService.load(node.getId(), taxonNodePropertyPath);
192 taxonNode = findTaxonNodeForDescription(node, specimen);
193 if(taxonNode!=null){
194 break;
195 }
196 else{
197 //check for child nodes
198 List<TaxonNode> allChildren = taxonNodeService.loadChildNodesOfTaxonNode(node, taxonNodePropertyPath, true, true, null);
199 for (TaxonNode child : allChildren) {
200 taxonNode = findTaxonNodeForDescription(child, specimen);
201 if(taxonNode!=null){
202 break;
203 }
204 }
205 }
206 }
207 if(taxonNode==null){
208 return null;
209 }
210 //taxon node was found
211
212 //get field unit
213 Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
214 Arrays.asList(new String[]{
215 "gatheringEvent",
216 "gatheringEvent.country"
217 }));
218 if(fieldUnits.size()!=1){
219 logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
220 return null;
221 }
222 else{
223 fieldUnit = fieldUnits.iterator().next();
224 }
225 //get identifier
226 if(HibernateProxyHelper.isInstanceOf(specimen, DerivedUnit.class)){
227 identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
228 }
229 //get country
230 if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
231 country = fieldUnit.getGatheringEvent().getCountry();
232 }
233 //get default taxon description
234 TaxonDescription defaultTaxonDescription = findDefaultTaxonDescription(descriptiveDataSet,
235 taxonNode);
236 TaxonRowWrapperDTO taxonRowWrapper = defaultTaxonDescription != null
237 ? createTaxonRowWrapper(defaultTaxonDescription.getUuid(), descriptiveDataSet.getUuid()) : null;
238 return new SpecimenRowWrapperDTO(description, taxonNode, fieldUnit, identifier, country, taxonRowWrapper);
239 }
240
241 @Override
242 @Transactional(readOnly = false)
243 public void updateTitleCache(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
244 IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
245 if (clazz == null) {
246 clazz = DescriptiveDataSet.class;
247 }
248 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
249 }
250
251 /**
252 * Returns a {@link TaxonDescription} for a given taxon node with corresponding
253 * features according to the {@link DescriptiveDataSet}.<br>
254 * If a description is found that matches all features of the data set this description
255 * will be returned.
256 * @param descriptiveDataSetUuid the uuid of the dataset defining the features
257 * @param taxonNodeUuid the uuid of the taxon node that links to the taxon
258 * if none could be found
259 * @return the found taxon description or <code>null</code>
260 */
261 private TaxonDescription findDefaultTaxonDescription(DescriptiveDataSet dataSet, TaxonNode taxonNode){
262 Set<DescriptionBase> dataSetDescriptions = dataSet.getDescriptions();
263 //filter out COMPUTED descriptions
264 List<TaxonDescription> nonComputedDescriptions = taxonNode.getTaxon().getDescriptions().stream()
265 .filter(desc -> desc.getMarkers().stream()
266 .noneMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
267 .collect(Collectors.toList());
268 for (TaxonDescription taxonDescription : nonComputedDescriptions) {
269 for (DescriptionBase description : dataSetDescriptions) {
270 if(description.getUuid().equals(taxonDescription.getUuid())){
271 return HibernateProxyHelper.deproxy(descriptionService.load(taxonDescription.getUuid(),
272 Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
273 }
274 }
275 }
276 return null;
277 }
278
279 @Override
280 @Transactional(readOnly=false)
281 public UpdateResult aggregateTaxonDescription(UUID taxonNodeUuid, UUID descriptiveDataSetUuid,
282 IRemotingProgressMonitor monitor){
283 UpdateResult result = new UpdateResult();
284
285 TaxonNode node = taxonNodeService.load(taxonNodeUuid);
286 Taxon taxon = HibernateProxyHelper.deproxy(taxonService.load(node.getTaxon().getUuid()), Taxon.class);
287 result.setCdmEntity(taxon);
288
289 //get all "computed" descriptions from all sub nodes
290 List<TaxonNode> childNodes = taxonNodeService.listChildrenOf(node, null, null, true, false, null);
291 List<TaxonDescription> computedDescriptions = new ArrayList<>();
292
293 childNodes.stream().map(childNode -> childNode.getTaxon())
294 .forEach(childTaxon -> childTaxon.getDescriptions().stream()
295 // filter out non-computed descriptions
296 .filter(description -> description.getMarkers().stream()
297 .anyMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
298 // add them to the list
299 .forEach(computedDescription -> computedDescriptions.add(computedDescription)));
300
301 UpdateResult aggregateDescription = aggregateDescription(taxon, computedDescriptions,
302 "[Taxon Descriptions]"+taxon.getTitleCache(), descriptiveDataSetUuid);
303 result.includeResult(aggregateDescription);
304 result.setCdmEntity(aggregateDescription.getCdmEntity());
305 aggregateDescription.setCdmEntity(null);
306 return result;
307 }
308
309 @Override
310 @Transactional(readOnly=false)
311 public UpdateResult aggregateDescription(UUID taxonUuid, List<UUID> descriptionUuids, String descriptionTitle
312 , UUID descriptiveDataSetUuid) {
313 UpdateResult result = new UpdateResult();
314
315 TaxonBase taxonBase = taxonService.load(taxonUuid);
316 if(!(taxonBase instanceof Taxon)){
317 result.addException(new ClassCastException("The given taxonUUID does not belong to a taxon"));
318 result.setError();
319 return result;
320 }
321 Taxon taxon = (Taxon)taxonBase;
322
323 List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
324
325 UpdateResult aggregateDescriptionResult = aggregateDescription(taxon, descriptions, descriptionTitle, descriptiveDataSetUuid);
326 result.setCdmEntity(aggregateDescriptionResult.getCdmEntity());
327 aggregateDescriptionResult.setCdmEntity(null);
328 result.includeResult(aggregateDescriptionResult);
329 return result;
330 }
331
332 @SuppressWarnings("unchecked")
333 private UpdateResult aggregateDescription(Taxon taxon, List<? extends DescriptionBase> descriptions, String descriptionTitle
334 , UUID descriptiveDataSetUuid) {
335 UpdateResult result = new UpdateResult();
336 Map<Character, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
337
338 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
339 if(dataSet==null){
340 result.addException(new IllegalArgumentException("Could not find data set for uuid "+descriptiveDataSetUuid));
341 result.setAbort();
342 return result;
343 }
344
345 //extract all character description elements
346 descriptions.forEach(description->{
347 description.getElements()
348 .stream()
349 //filter out elements that do not have a Characters as Feature
350 .filter(element->HibernateProxyHelper.isInstanceOf(((DescriptionElementBase)element).getFeature(), Character.class))
351 .forEach(ele->{
352 DescriptionElementBase descriptionElement = (DescriptionElementBase)ele;
353 List<DescriptionElementBase> list = featureToElementMap.get(descriptionElement.getFeature());
354 if(list==null){
355 list = new ArrayList<>();
356 }
357 list.add(descriptionElement);
358 featureToElementMap.put(HibernateProxyHelper.deproxy(descriptionElement.getFeature(), Character.class), list);
359 });
360 });
361
362 TaxonDescription description = TaxonDescription.NewInstance(taxon);
363 description.setTitleCache("[Aggregation] "+descriptionTitle, true);
364 description.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true));
365 IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Aggregation);
366 description.addSource(source);
367 description.addDescriptiveDataSet(dataSet);
368
369 featureToElementMap.forEach((feature, elements)->{
370 //aggregate categorical data
371 if(feature.isSupportsCategoricalData()){
372 CategoricalData aggregate = CategoricalData.NewInstance(feature);
373 elements.stream()
374 .filter(element->element instanceof CategoricalData)
375 .forEach(categoricalData->((CategoricalData)categoricalData).getStateData()
376 .forEach(stateData->aggregate.addStateData((StateData) stateData.clone())));
377 description.addElement(aggregate);
378 }
379 //aggregate quantitative data
380 else if(feature.isSupportsQuantitativeData()){
381 QuantitativeData aggregate = QuantitativeData.NewInstance(feature);
382 elements.stream()
383 .filter(element->element instanceof QuantitativeData)
384 .forEach(categoricalData->((QuantitativeData)categoricalData).getStatisticalValues()
385 .forEach(statisticalValue->aggregate.addStatisticalValue((StatisticalMeasurementValue) statisticalValue.clone())));
386 description.addElement(aggregate);
387 }
388 });
389 result.addUpdatedObject(taxon);
390 result.setCdmEntity(description);
391 return result;
392 }
393
394 @Override
395 @Transactional(readOnly=false)
396 public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, MarkerType markerType, boolean markerFlag){
397 DescriptiveDataSet dataSet = load(dataSetUuid);
398 TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon"));
399 TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
400 String tag = "";
401 if(markerFlag){
402 if(markerType.equals(MarkerType.USE())){
403 tag = "[Default]";
404 }
405 else if(markerType.equals(MarkerType.IN_BIBLIOGRAPHY())){
406 tag = "[Literature]";
407 }
408 }
409 newTaxonDescription.setTitleCache(tag+" "+dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
410 if(markerType!=null){
411 newTaxonDescription.addMarker(Marker.NewInstance(markerType, markerFlag));
412 }
413 dataSet.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature->{
414 if(wsFeature.isSupportsCategoricalData()){
415 newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
416 }
417 else if(wsFeature.isSupportsQuantitativeData()){
418 newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
419 }
420 });
421 dataSet.addDescription(newTaxonDescription);
422
423 return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
424 }
425
426 @Override
427 @Transactional(readOnly=false)
428 public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, UUID specimenUuid, boolean addDatasetSource){
429 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
430 SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
431
432 Set<Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
433 List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
434
435 for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
436 specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
437
438 //check if description is already added to data set
439 if(dataSet.getDescriptions().contains(specimenDescription)){
440 return specimenDescription;
441 }
442
443 //gather specimen description features and check for match with dataset features
444 Set<Feature> specimenDescriptionFeatures = new HashSet<>();
445 for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
446 Feature feature = specimenDescriptionElement.getFeature();
447 specimenDescriptionFeatures.add(feature);
448 if(datasetFeatures.contains(feature)){
449 matchingDescriptionElements.add(specimenDescriptionElement);
450 }
451 }
452 }
453 //Create new specimen description if description has not already been added to the dataset
454 SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
455 newDesription.setTitleCache("Dataset "+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
456
457 //check for equals description element (same feature and same values)
458 Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
459 for(DescriptionElementBase element:matchingDescriptionElements){
460 List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
461 if(list==null){
462 list = new ArrayList<>();
463 }
464 list.add(element);
465 featureToElementMap.put(element.getFeature(), list);
466 }
467 Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
468 for(Feature feature:featureToElementMap.keySet()){
469 List<DescriptionElementBase> elements = featureToElementMap.get(feature);
470 //no duplicate description elements found for this feature
471 if(elements.size()==1){
472 descriptionElementsToClone.add(elements.get(0));
473 }
474 //duplicates found -> check if all are equal
475 else{
476 DescriptionElementBase match = null;
477 for (DescriptionElementBase descriptionElementBase : elements) {
478 if(match==null){
479 match = descriptionElementBase;
480 }
481 else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
482 match = null;
483 //TODO: propagate message
484 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
485 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
486 break;
487 }
488 }
489 if(match!=null){
490 descriptionElementsToClone.add(match);
491 }
492 }
493 }
494 //clone matching descriptionElements
495 for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
496 DescriptionElementBase clone;
497 try {
498 clone = descriptionElementBase.clone(newDesription);
499 clone.getSources().forEach(source -> {
500 if(descriptionElementBase instanceof CategoricalData){
501 TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, null);
502 source.setOriginalNameString(label.getText(Language.DEFAULT()));
503 }
504 else if(descriptionElementBase instanceof QuantitativeData){
505 TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, null);
506 source.setOriginalNameString(label.getText(Language.DEFAULT()));
507 }
508 });
509 } catch (CloneNotSupportedException e) {
510 //nothing
511 }
512 }
513
514 //add all remaining description elements to the new description
515 for(Feature wsFeature:datasetFeatures){
516 boolean featureFound = false;
517 for(DescriptionElementBase element:newDesription.getElements()){
518 if(element.getFeature().equals(wsFeature)){
519 featureFound = true;
520 break;
521 }
522 }
523 if(!featureFound){
524 if(wsFeature.isSupportsCategoricalData()){
525 newDesription.addElement(CategoricalData.NewInstance(wsFeature));
526 }
527 else if(wsFeature.isSupportsQuantitativeData()){
528 newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
529 }
530 }
531 }
532 //add sources of data set
533 if(addDatasetSource){
534 dataSet.getSources().forEach(source->{
535 try {
536 newDesription.addSource((IdentifiableSource) source.clone());
537 } catch (CloneNotSupportedException e) {
538 //nothing
539 }
540 });
541 }
542 return newDesription;
543
544 }
545
546 //TODO: this should either be solved in the model class itself
547 //OR this should cover all possibilities including modifiers for example
548 private class DescriptionElementCompareWrapper {
549
550 private DescriptionElementBase element;
551 private Set<UUID> stateUuids = new HashSet<>();
552 private Set<Float> avgs = new HashSet<>();
553 private Set<Float> exacts = new HashSet<>();
554 private Set<Float> maxs = new HashSet<>();
555 private Set<Float> mins = new HashSet<>();
556 private Set<Float> sampleSizes = new HashSet<>();
557 private Set<Float> standardDevs = new HashSet<>();
558 private Set<Float> lowerBounds = new HashSet<>();
559 private Set<Float> upperBounds = new HashSet<>();
560 private Set<Float> variances = new HashSet<>();
561
562 public DescriptionElementCompareWrapper(DescriptionElementBase element) {
563 this.element = element;
564 if(element.isInstanceOf(CategoricalData.class)){
565 CategoricalData elementData = (CategoricalData)element;
566 elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
567 }
568 else if(element.isInstanceOf(QuantitativeData.class)){
569 QuantitativeData elementData = (QuantitativeData)element;
570 elementData.getStatisticalValues().forEach(value->{
571 if(value.getType().equals(StatisticalMeasure.AVERAGE())){
572 avgs.add(value.getValue());
573 }
574 else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
575 exacts.add(value.getValue());
576
577 }
578 else if(value.getType().equals(StatisticalMeasure.MAX())){
579 maxs.add(value.getValue());
580 }
581 else if(value.getType().equals(StatisticalMeasure.MIN())){
582 mins.add(value.getValue());
583 }
584 else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
585 sampleSizes.add(value.getValue());
586
587 }
588 else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
589 standardDevs.add(value.getValue());
590 }
591 else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
592 lowerBounds.add(value.getValue());
593
594 }
595 else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
596 upperBounds.add(value.getValue());
597 }
598 else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
599 variances.add(value.getValue());
600 }
601 });
602 }
603 }
604
605 @Override
606 public int hashCode() {
607 final int prime = 31;
608 int result = 1;
609 result = prime * result + getOuterType().hashCode();
610 result = prime * result + ((avgs == null) ? 0 : avgs.hashCode());
611 result = prime * result + ((element == null) ? 0 : element.hashCode());
612 result = prime * result + ((exacts == null) ? 0 : exacts.hashCode());
613 result = prime * result + ((lowerBounds == null) ? 0 : lowerBounds.hashCode());
614 result = prime * result + ((maxs == null) ? 0 : maxs.hashCode());
615 result = prime * result + ((mins == null) ? 0 : mins.hashCode());
616 result = prime * result + ((sampleSizes == null) ? 0 : sampleSizes.hashCode());
617 result = prime * result + ((standardDevs == null) ? 0 : standardDevs.hashCode());
618 result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
619 result = prime * result + ((upperBounds == null) ? 0 : upperBounds.hashCode());
620 result = prime * result + ((variances == null) ? 0 : variances.hashCode());
621 return result;
622 }
623
624 @Override
625 public boolean equals(Object obj) {
626 if (this == obj) {
627 return true;
628 }
629 if (obj == null) {
630 return false;
631 }
632 if (getClass() != obj.getClass()) {
633 return false;
634 }
635 DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
636 if (!getOuterType().equals(other.getOuterType())) {
637 return false;
638 }
639 if (avgs == null) {
640 if (other.avgs != null) {
641 return false;
642 }
643 } else if (!avgs.equals(other.avgs)) {
644 return false;
645 }
646 if (element == null) {
647 if (other.element != null) {
648 return false;
649 }
650 } else if (!element.equals(other.element)) {
651 return false;
652 }
653 if (exacts == null) {
654 if (other.exacts != null) {
655 return false;
656 }
657 } else if (!exacts.equals(other.exacts)) {
658 return false;
659 }
660 if (lowerBounds == null) {
661 if (other.lowerBounds != null) {
662 return false;
663 }
664 } else if (!lowerBounds.equals(other.lowerBounds)) {
665 return false;
666 }
667 if (maxs == null) {
668 if (other.maxs != null) {
669 return false;
670 }
671 } else if (!maxs.equals(other.maxs)) {
672 return false;
673 }
674 if (mins == null) {
675 if (other.mins != null) {
676 return false;
677 }
678 } else if (!mins.equals(other.mins)) {
679 return false;
680 }
681 if (sampleSizes == null) {
682 if (other.sampleSizes != null) {
683 return false;
684 }
685 } else if (!sampleSizes.equals(other.sampleSizes)) {
686 return false;
687 }
688 if (standardDevs == null) {
689 if (other.standardDevs != null) {
690 return false;
691 }
692 } else if (!standardDevs.equals(other.standardDevs)) {
693 return false;
694 }
695 if (stateUuids == null) {
696 if (other.stateUuids != null) {
697 return false;
698 }
699 } else if (!stateUuids.equals(other.stateUuids)) {
700 return false;
701 }
702 if (upperBounds == null) {
703 if (other.upperBounds != null) {
704 return false;
705 }
706 } else if (!upperBounds.equals(other.upperBounds)) {
707 return false;
708 }
709 if (variances == null) {
710 if (other.variances != null) {
711 return false;
712 }
713 } else if (!variances.equals(other.variances)) {
714 return false;
715 }
716 return true;
717 }
718
719 private DescriptiveDataSetService getOuterType() {
720 return DescriptiveDataSetService.this;
721 }
722
723 }
724
725 }