Merge branch 'release/5.6.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
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;
18
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;
59
60 @Service
61 @Transactional(readOnly=true)
62 public class DescriptiveDataSetService
63 extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
64 implements IDescriptiveDataSetService {
65
66 private static Logger logger = Logger.getLogger(DescriptiveDataSetService.class);
67
68 @Autowired
69 private IOccurrenceService occurrenceService;
70
71 @Autowired
72 private ITaxonService taxonService;
73
74 @Autowired
75 private IDescriptionService descriptionService;
76
77 @Autowired
78 private ITaxonNodeService taxonNodeService;
79
80 @Autowired
81 private IProgressMonitorService progressMonitorService;
82
83 //FIXME: Use actual MarkerType for default descriptions when implemented #7957
84 private MarkerType MARKER_DEFAULT = MarkerType.TO_BE_CHECKED();
85
86 @Override
87 @Autowired
88 protected void setDao(IDescriptiveDataSetDao dao) {
89 this.dao = dao;
90 }
91
92 @Override
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);
96 }
97
98 @Override
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);
102 }
103
104 @Override
105 public List<UuidAndTitleCache<DescriptiveDataSet>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements, String pattern) {
106 return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
107 }
108
109 @Override
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<>();
117 }
118 RowWrapperDTO rowWrapper = null;
119 if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class)){
120 rowWrapper = createTaxonRowWrapper(description.getUuid(), descriptiveDataSet.getUuid());
121 }
122 else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)){
123 rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSet);
124 }
125 if(rowWrapper!=null){
126 wrappers.add(rowWrapper);
127 }
128 monitor.worked(1);
129 }
130 return wrappers;
131 }
132
133 @Override
134 public Collection<SpecimenNodeWrapper> loadSpecimens(DescriptiveDataSet descriptiveDataSet){
135 List<UUID> filteredNodes = findFilteredTaxonNodes(descriptiveDataSet);
136 return occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
137 }
138
139 @Override
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);
145
146 return taxonNodeService.uuidList(filter);
147 }
148
149 @Override
150 public List<TaxonNode> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet, List<String> propertyPaths){
151 return taxonNodeService.load(findFilteredTaxonNodes(descriptiveDataSet), propertyPaths);
152 }
153
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())){
158 return taxonNode;
159 }
160 }
161 return null;
162 }
163
164 @Override
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);
178 }
179 return new TaxonRowWrapperDTO(description, new TaxonNodeDto(taxonNode));
180 }
181
182 @Override
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
190 //get taxon node
191 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
192 for (TaxonNode node : taxonSubtreeFilter) {
193 //check for node
194 List<String> taxonNodePropertyPath = Arrays.asList("taxon", "taxon.descriptions", "taxon.descriptions.markers");
195 node = taxonNodeService.load(node.getId(), taxonNodePropertyPath);
196 taxonNode = findTaxonNodeForDescription(node, specimen);
197 if(taxonNode!=null){
198 break;
199 }
200 else{
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);
205 if(taxonNode!=null){
206 break;
207 }
208 }
209 }
210 }
211 if(taxonNode==null){
212 return null;
213 }
214 //taxon node was found
215
216 //get field unit
217 Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
218 Arrays.asList(new String[]{
219 "gatheringEvent",
220 "gatheringEvent.country"
221 }));
222 if(fieldUnits.size()!=1){
223 logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
224 return null;
225 }
226 else{
227 fieldUnit = fieldUnits.iterator().next();
228 }
229 //get identifier
230 if(HibernateProxyHelper.isInstanceOf(specimen, DerivedUnit.class)){
231 identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
232 }
233 //get country
234 if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
235 country = fieldUnit.getGatheringEvent().getCountry();
236 }
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);
243 }
244
245 @Override
246 @Transactional(readOnly = false)
247 public UpdateResult updateCaches(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
248 IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
249 if (clazz == null) {
250 clazz = DescriptiveDataSet.class;
251 }
252 return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
253 }
254
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))
261 .findFirst();
262 if(first.isPresent()){
263 return HibernateProxyHelper.deproxy(descriptionService.load(first.get().getUuid(),
264 Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
265 }
266 return null;
267 }
268
269 @Override
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);
274 }
275
276 @Override
277 @Transactional(readOnly=false)
278 public UpdateResult aggregateTaxonDescription(UUID taxonNodeUuid, UUID descriptiveDataSetUuid,
279 IRemotingProgressMonitor monitor){
280 UpdateResult result = new UpdateResult();
281
282 TaxonNode node = taxonNodeService.load(taxonNodeUuid);
283 Taxon taxon = HibernateProxyHelper.deproxy(taxonService.load(node.getTaxon().getUuid()), Taxon.class);
284 result.setCdmEntity(taxon);
285
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<>();
289
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)));
297
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);
303 return result;
304 }
305
306 @Override
307 @Transactional(readOnly=false)
308 public UpdateResult aggregateDescription(UUID taxonUuid, List<UUID> descriptionUuids, String descriptionTitle
309 , UUID descriptiveDataSetUuid) {
310 UpdateResult result = new UpdateResult();
311
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"));
315 result.setError();
316 return result;
317 }
318 Taxon taxon = (Taxon)taxonBase;
319
320 List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
321
322 UpdateResult aggregateDescriptionResult = aggregateDescription(taxon, descriptions, descriptionTitle, descriptiveDataSetUuid);
323 result.setCdmEntity(aggregateDescriptionResult.getCdmEntity());
324 aggregateDescriptionResult.setCdmEntity(null);
325 result.includeResult(aggregateDescriptionResult);
326 return result;
327 }
328
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<>();
334
335 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
336 if(dataSet==null){
337 result.addException(new IllegalArgumentException("Could not find data set for uuid "+descriptiveDataSetUuid));
338 result.setAbort();
339 return result;
340 }
341
342 //extract all character description elements
343 descriptions.forEach(description->{
344 description.getElements()
345 .stream()
346 //filter out elements that do not have a Characters as Feature
347 .filter(element->HibernateProxyHelper.isInstanceOf(((DescriptionElementBase)element).getFeature(), Character.class))
348 .forEach(ele->{
349 DescriptionElementBase descriptionElement = (DescriptionElementBase)ele;
350 List<DescriptionElementBase> list = featureToElementMap.get(descriptionElement.getFeature());
351 if(list==null){
352 list = new ArrayList<>();
353 }
354 list.add(descriptionElement);
355 featureToElementMap.put(HibernateProxyHelper.deproxy(descriptionElement.getFeature(), Character.class), list);
356 });
357 });
358
359 // delete existing aggregation description, if present
360 TaxonDescription aggregation = findTaxonDescriptionByMarkerType(dataSet, taxon, MarkerType.COMPUTED());
361 if(aggregation!=null){
362 removeDescription(aggregation.getUuid(), descriptiveDataSetUuid);
363 }
364
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);
372
373 featureToElementMap.forEach((feature, elements)->{
374 //aggregate categorical data
375 if(feature.isSupportsCategoricalData()){
376 CategoricalData aggregate = CategoricalData.NewInstance(feature);
377 elements.stream()
378 .filter(element->element instanceof CategoricalData)
379 .forEach(categoricalData->((CategoricalData)categoricalData).getStateData()
380 .forEach(stateData->aggregate.addStateData((StateData) stateData.clone())));
381 description.addElement(aggregate);
382 }
383 //aggregate quantitative data
384 else if(feature.isSupportsQuantitativeData()){
385 QuantitativeData aggregate = QuantitativeData.NewInstance(feature);
386 elements.stream()
387 .filter(element->element instanceof QuantitativeData)
388 .forEach(categoricalData->((QuantitativeData)categoricalData).getStatisticalValues()
389 .forEach(statisticalValue->aggregate.addStatisticalValue((StatisticalMeasurementValue) statisticalValue.clone())));
390 description.addElement(aggregate);
391 }
392 });
393 result.addUpdatedObject(taxon);
394 result.setCdmEntity(description);
395 return result;
396 }
397
398 @Override
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){
405 result.setError();
406 }
407 else{
408 boolean success = dataSet.removeDescription(descriptionBase);
409 result.addDeletedObject(descriptionBase);
410 result.addUpdatedObject(dataSet);
411 result.setStatus(success?Status.OK:Status.ERROR);
412 }
413 return result;
414 }
415
416 @Override
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());
422 String tag = "";
423 if(markerFlag){
424 //FIXME: Add specific MarkerTypes to enum (see #7957)
425 if(markerType.equals(MARKER_DEFAULT)){
426 tag = "[Default]";
427 }
428 else if(markerType.equals(MarkerType.IN_BIBLIOGRAPHY())){
429 tag = "[Literature]";
430 }
431 }
432 newTaxonDescription.setTitleCache(tag+" "+dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
433 if(markerType!=null){
434 newTaxonDescription.addMarker(Marker.NewInstance(markerType, markerFlag));
435 }
436 dataSet.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature->{
437 if(wsFeature.isSupportsCategoricalData()){
438 newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
439 }
440 else if(wsFeature.isSupportsQuantitativeData()){
441 newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
442 }
443 });
444 dataSet.addDescription(newTaxonDescription);
445
446 return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
447 }
448
449 @Override
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);
454
455 Set<Character> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
456 List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
457
458 for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
459 specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
460
461 //check if description is already added to data set
462 if(dataSet.getDescriptions().contains(specimenDescription)){
463 return specimenDescription;
464 }
465
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);
473 }
474 }
475 }
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$
479
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());
484 if(list==null){
485 list = new ArrayList<>();
486 }
487 list.add(element);
488 featureToElementMap.put(element.getFeature(), list);
489 }
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));
496 }
497 //duplicates found -> check if all are equal
498 else{
499 DescriptionElementBase match = null;
500 for (DescriptionElementBase descriptionElementBase : elements) {
501 if(match==null){
502 match = descriptionElementBase;
503 }
504 else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
505 match = null;
506 //TODO: propagate message
507 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
508 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
509 break;
510 }
511 }
512 if(match!=null){
513 descriptionElementsToClone.add(match);
514 }
515 }
516 }
517 //clone matching descriptionElements
518 for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
519 DescriptionElementBase clone;
520 try {
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()));
526 }
527 else if(descriptionElementBase instanceof QuantitativeData){
528 TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, null);
529 source.setOriginalNameString(label.getText(Language.DEFAULT()));
530 }
531 });
532 } catch (CloneNotSupportedException e) {
533 //nothing
534 }
535 }
536
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)){
542 featureFound = true;
543 break;
544 }
545 }
546 if(!featureFound){
547 if(wsFeature.isSupportsCategoricalData()){
548 newDesription.addElement(CategoricalData.NewInstance(wsFeature));
549 }
550 else if(wsFeature.isSupportsQuantitativeData()){
551 newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
552 }
553 }
554 }
555 //add sources of data set
556 if(addDatasetSource){
557 dataSet.getSources().forEach(source->{
558 try {
559 newDesription.addSource((IdentifiableSource) source.clone());
560 } catch (CloneNotSupportedException e) {
561 //nothing
562 }
563 });
564 }
565 return newDesription;
566
567 }
568
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 {
572
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<>();
584
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()));
590 }
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());
596 }
597 else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
598 exacts.add(value.getValue());
599
600 }
601 else if(value.getType().equals(StatisticalMeasure.MAX())){
602 maxs.add(value.getValue());
603 }
604 else if(value.getType().equals(StatisticalMeasure.MIN())){
605 mins.add(value.getValue());
606 }
607 else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
608 sampleSizes.add(value.getValue());
609
610 }
611 else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
612 standardDevs.add(value.getValue());
613 }
614 else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
615 lowerBounds.add(value.getValue());
616
617 }
618 else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
619 upperBounds.add(value.getValue());
620 }
621 else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
622 variances.add(value.getValue());
623 }
624 });
625 }
626 }
627
628 @Override
629 public int hashCode() {
630 final int prime = 31;
631 int result = 1;
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());
644 return result;
645 }
646
647 @Override
648 public boolean equals(Object obj) {
649 if (this == obj) {
650 return true;
651 }
652 if (obj == null) {
653 return false;
654 }
655 if (getClass() != obj.getClass()) {
656 return false;
657 }
658 DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
659 if (!getOuterType().equals(other.getOuterType())) {
660 return false;
661 }
662 if (avgs == null) {
663 if (other.avgs != null) {
664 return false;
665 }
666 } else if (!avgs.equals(other.avgs)) {
667 return false;
668 }
669 if (element == null) {
670 if (other.element != null) {
671 return false;
672 }
673 } else if (!element.equals(other.element)) {
674 return false;
675 }
676 if (exacts == null) {
677 if (other.exacts != null) {
678 return false;
679 }
680 } else if (!exacts.equals(other.exacts)) {
681 return false;
682 }
683 if (lowerBounds == null) {
684 if (other.lowerBounds != null) {
685 return false;
686 }
687 } else if (!lowerBounds.equals(other.lowerBounds)) {
688 return false;
689 }
690 if (maxs == null) {
691 if (other.maxs != null) {
692 return false;
693 }
694 } else if (!maxs.equals(other.maxs)) {
695 return false;
696 }
697 if (mins == null) {
698 if (other.mins != null) {
699 return false;
700 }
701 } else if (!mins.equals(other.mins)) {
702 return false;
703 }
704 if (sampleSizes == null) {
705 if (other.sampleSizes != null) {
706 return false;
707 }
708 } else if (!sampleSizes.equals(other.sampleSizes)) {
709 return false;
710 }
711 if (standardDevs == null) {
712 if (other.standardDevs != null) {
713 return false;
714 }
715 } else if (!standardDevs.equals(other.standardDevs)) {
716 return false;
717 }
718 if (stateUuids == null) {
719 if (other.stateUuids != null) {
720 return false;
721 }
722 } else if (!stateUuids.equals(other.stateUuids)) {
723 return false;
724 }
725 if (upperBounds == null) {
726 if (other.upperBounds != null) {
727 return false;
728 }
729 } else if (!upperBounds.equals(other.upperBounds)) {
730 return false;
731 }
732 if (variances == null) {
733 if (other.variances != null) {
734 return false;
735 }
736 } else if (!variances.equals(other.variances)) {
737 return false;
738 }
739 return true;
740 }
741
742 private DescriptiveDataSetService getOuterType() {
743 return DescriptiveDataSetService.this;
744 }
745
746 }
747
748 }