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