Project

General

Profile

Download (33.6 KB) Statistics
| Branch: | Tag: | Revision:
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.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.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 void updateTitleCache(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
248
            IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
249
        if (clazz == null) {
250
            clazz = DescriptiveDataSet.class;
251
        }
252
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
253
    }
254

    
255
    @Override
256
    public TaxonDescription findTaxonDescriptionByMarkerType(UUID dataSetUuid, UUID taxonNodeUuid, MarkerType markerType){
257
        DescriptiveDataSet dataSet = load(dataSetUuid);
258
        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
259
        Set<DescriptionBase> dataSetDescriptions = dataSet.getDescriptions();
260
        //filter by DEFAULT descriptions
261
        Optional<TaxonDescription> first = taxonNode.getTaxon().getDescriptions().stream()
262
                .filter(desc -> desc.getMarkers().stream().anyMatch(marker -> marker.getMarkerType().equals(markerType)))
263
                .filter(defaultDescription->dataSetDescriptions.contains(defaultDescription))
264
                .findFirst();
265
        if(first.isPresent()){
266
            return HibernateProxyHelper.deproxy(descriptionService.load(first.get().getUuid(),
267
                  Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
268
        }
269
        return null;
270
    }
271

    
272
    @Override
273
    @Transactional(readOnly=false)
274
    public UpdateResult aggregateTaxonDescription(UUID taxonNodeUuid, UUID descriptiveDataSetUuid,
275
            IRemotingProgressMonitor monitor){
276
        UpdateResult result = new UpdateResult();
277

    
278
        TaxonNode node = taxonNodeService.load(taxonNodeUuid);
279
        Taxon taxon = HibernateProxyHelper.deproxy(taxonService.load(node.getTaxon().getUuid()), Taxon.class);
280
        result.setCdmEntity(taxon);
281

    
282
        //get all "computed" descriptions from all sub nodes
283
        List<TaxonNode> childNodes = taxonNodeService.listChildrenOf(node, null, null, true, false, null);
284
        List<TaxonDescription> computedDescriptions = new ArrayList<>();
285

    
286
        childNodes.stream().map(childNode -> childNode.getTaxon())
287
                .forEach(childTaxon -> childTaxon.getDescriptions().stream()
288
                        // filter out non-computed descriptions
289
                        .filter(description -> description.getMarkers().stream()
290
                                .anyMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
291
                        // add them to the list
292
                        .forEach(computedDescription -> computedDescriptions.add(computedDescription)));
293

    
294
        UpdateResult aggregateDescription = aggregateDescription(taxon, computedDescriptions,
295
                "[Taxon Descriptions]"+taxon.getTitleCache(), descriptiveDataSetUuid);
296
        result.includeResult(aggregateDescription);
297
        result.setCdmEntity(aggregateDescription.getCdmEntity());
298
        aggregateDescription.setCdmEntity(null);
299
        return result;
300
    }
301

    
302
    @Override
303
    @Transactional(readOnly=false)
304
    public UpdateResult aggregateDescription(UUID taxonUuid, List<UUID> descriptionUuids, String descriptionTitle
305
            , UUID descriptiveDataSetUuid) {
306
        UpdateResult result = new UpdateResult();
307

    
308
        TaxonBase taxonBase = taxonService.load(taxonUuid);
309
        if(!(taxonBase instanceof Taxon)){
310
            result.addException(new ClassCastException("The given taxonUUID does not belong to a taxon"));
311
            result.setError();
312
            return result;
313
        }
314
        Taxon taxon = (Taxon)taxonBase;
315

    
316
        List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
317

    
318
        UpdateResult aggregateDescriptionResult = aggregateDescription(taxon, descriptions, descriptionTitle, descriptiveDataSetUuid);
319
        result.setCdmEntity(aggregateDescriptionResult.getCdmEntity());
320
        aggregateDescriptionResult.setCdmEntity(null);
321
        result.includeResult(aggregateDescriptionResult);
322
        return result;
323
    }
324

    
325
    @SuppressWarnings("unchecked")
326
    private UpdateResult aggregateDescription(Taxon taxon, List<? extends DescriptionBase> descriptions, String descriptionTitle
327
            , UUID descriptiveDataSetUuid) {
328
        UpdateResult result = new UpdateResult();
329
        Map<Character, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
330

    
331
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
332
        if(dataSet==null){
333
            result.addException(new IllegalArgumentException("Could not find data set for uuid "+descriptiveDataSetUuid));
334
            result.setAbort();
335
            return result;
336
        }
337

    
338
        //extract all character description elements
339
        descriptions.forEach(description->{
340
            description.getElements()
341
            .stream()
342
            //filter out elements that do not have a Characters as Feature
343
            .filter(element->HibernateProxyHelper.isInstanceOf(((DescriptionElementBase)element).getFeature(), Character.class))
344
            .forEach(ele->{
345
                DescriptionElementBase descriptionElement = (DescriptionElementBase)ele;
346
                List<DescriptionElementBase> list = featureToElementMap.get(descriptionElement.getFeature());
347
                if(list==null){
348
                    list = new ArrayList<>();
349
                }
350
                list.add(descriptionElement);
351
                featureToElementMap.put(HibernateProxyHelper.deproxy(descriptionElement.getFeature(), Character.class), list);
352
            });
353
        });
354

    
355
        TaxonDescription description = TaxonDescription.NewInstance(taxon);
356
        description.setTitleCache("[Aggregation] "+descriptionTitle, true);
357
        description.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true));
358
        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Aggregation);
359
        description.addSource(source);
360
        description.addDescriptiveDataSet(dataSet);
361

    
362
        featureToElementMap.forEach((feature, elements)->{
363
            //aggregate categorical data
364
            if(feature.isSupportsCategoricalData()){
365
                CategoricalData aggregate = CategoricalData.NewInstance(feature);
366
                elements.stream()
367
                .filter(element->element instanceof CategoricalData)
368
                .forEach(categoricalData->((CategoricalData)categoricalData).getStateData()
369
                        .forEach(stateData->aggregate.addStateData((StateData) stateData.clone())));
370
                description.addElement(aggregate);
371
            }
372
            //aggregate quantitative data
373
            else if(feature.isSupportsQuantitativeData()){
374
                QuantitativeData aggregate = QuantitativeData.NewInstance(feature);
375
                elements.stream()
376
                .filter(element->element instanceof QuantitativeData)
377
                .forEach(categoricalData->((QuantitativeData)categoricalData).getStatisticalValues()
378
                        .forEach(statisticalValue->aggregate.addStatisticalValue((StatisticalMeasurementValue) statisticalValue.clone())));
379
                description.addElement(aggregate);
380
            }
381
        });
382
        result.addUpdatedObject(taxon);
383
        result.setCdmEntity(description);
384
        return result;
385
    }
386

    
387
    @Override
388
    @Transactional(readOnly=false)
389
    public DeleteResult removeDescription(UUID descriptionUuid, UUID descriptiveDataSetUuid) {
390
        DeleteResult result = new DeleteResult();
391
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
392
        DescriptionBase descriptionBase = descriptionService.load(descriptionUuid);
393
        if(dataSet==null || descriptionBase==null){
394
            result.setError();
395
        }
396
        else{
397
            boolean success = dataSet.removeDescription(descriptionBase);
398
            result.addDeletedObject(descriptionBase);
399
            result.addUpdatedObject(dataSet);
400
            result.setStatus(success?Status.OK:Status.ERROR);
401
        }
402
        return result;
403
    }
404

    
405
    @Override
406
    @Transactional(readOnly=false)
407
    public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, MarkerType markerType, boolean markerFlag){
408
        DescriptiveDataSet dataSet = load(dataSetUuid);
409
        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon"));
410
        TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
411
        String tag = "";
412
        if(markerFlag){
413
            //FIXME: Add specific MarkerTypes to enum (see #7957)
414
            if(markerType.equals(MARKER_DEFAULT)){
415
                tag = "[Default]";
416
            }
417
            else if(markerType.equals(MarkerType.IN_BIBLIOGRAPHY())){
418
                tag = "[Literature]";
419
            }
420
        }
421
        newTaxonDescription.setTitleCache(tag+" "+dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
422
        if(markerType!=null){
423
            newTaxonDescription.addMarker(Marker.NewInstance(markerType, markerFlag));
424
        }
425
        dataSet.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature->{
426
            if(wsFeature.isSupportsCategoricalData()){
427
                newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
428
            }
429
            else if(wsFeature.isSupportsQuantitativeData()){
430
                newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
431
            }
432
        });
433
        dataSet.addDescription(newTaxonDescription);
434

    
435
        return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
436
    }
437

    
438
    @Override
439
    @Transactional(readOnly=false)
440
    public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, UUID specimenUuid, boolean addDatasetSource){
441
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
442
        SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
443

    
444
        Set<Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
445
        List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
446

    
447
        for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
448
            specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
449

    
450
            //check if description is already added to data set
451
            if(dataSet.getDescriptions().contains(specimenDescription)){
452
                return specimenDescription;
453
            }
454

    
455
            //gather specimen description features and check for match with dataset features
456
            Set<Feature> specimenDescriptionFeatures = new HashSet<>();
457
            for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
458
                Feature feature = specimenDescriptionElement.getFeature();
459
                specimenDescriptionFeatures.add(feature);
460
                if(datasetFeatures.contains(feature)){
461
                    matchingDescriptionElements.add(specimenDescriptionElement);
462
                }
463
            }
464
        }
465
        //Create new specimen description if description has not already been added to the dataset
466
        SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
467
        newDesription.setTitleCache("Dataset "+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
468

    
469
        //check for equals description element (same feature and same values)
470
        Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
471
        for(DescriptionElementBase element:matchingDescriptionElements){
472
            List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
473
            if(list==null){
474
                list = new ArrayList<>();
475
            }
476
            list.add(element);
477
            featureToElementMap.put(element.getFeature(), list);
478
        }
479
        Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
480
        for(Feature feature:featureToElementMap.keySet()){
481
            List<DescriptionElementBase> elements = featureToElementMap.get(feature);
482
            //no duplicate description elements found for this feature
483
            if(elements.size()==1){
484
                descriptionElementsToClone.add(elements.get(0));
485
            }
486
            //duplicates found -> check if all are equal
487
            else{
488
                DescriptionElementBase match = null;
489
                for (DescriptionElementBase descriptionElementBase : elements) {
490
                    if(match==null){
491
                        match = descriptionElementBase;
492
                    }
493
                    else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
494
                        match = null;
495
                        //TODO: propagate message
496
//                        MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
497
//                                String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
498
                        break;
499
                    }
500
                }
501
                if(match!=null){
502
                    descriptionElementsToClone.add(match);
503
                }
504
            }
505
        }
506
        //clone matching descriptionElements
507
        for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
508
            DescriptionElementBase clone;
509
            try {
510
                clone = descriptionElementBase.clone(newDesription);
511
                clone.getSources().forEach(source -> {
512
                    if(descriptionElementBase instanceof CategoricalData){
513
                        TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, null);
514
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
515
                    }
516
                    else if(descriptionElementBase instanceof QuantitativeData){
517
                        TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, null);
518
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
519
                    }
520
                });
521
            } catch (CloneNotSupportedException e) {
522
                //nothing
523
            }
524
        }
525

    
526
        //add all remaining description elements to the new description
527
        for(Feature wsFeature:datasetFeatures){
528
            boolean featureFound = false;
529
            for(DescriptionElementBase element:newDesription.getElements()){
530
                if(element.getFeature().equals(wsFeature)){
531
                    featureFound = true;
532
                    break;
533
                }
534
            }
535
            if(!featureFound){
536
                if(wsFeature.isSupportsCategoricalData()){
537
                    newDesription.addElement(CategoricalData.NewInstance(wsFeature));
538
                }
539
                else if(wsFeature.isSupportsQuantitativeData()){
540
                    newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
541
                }
542
            }
543
        }
544
        //add sources of data set
545
        if(addDatasetSource){
546
            dataSet.getSources().forEach(source->{
547
                try {
548
                    newDesription.addSource((IdentifiableSource) source.clone());
549
                } catch (CloneNotSupportedException e) {
550
                    //nothing
551
                }
552
            });
553
        }
554
        return newDesription;
555

    
556
    }
557

    
558
    //TODO: this should either be solved in the model class itself
559
    //OR this should cover all possibilities including modifiers for example
560
    private class DescriptionElementCompareWrapper {
561

    
562
        private DescriptionElementBase element;
563
        private Set<UUID> stateUuids = new HashSet<>();
564
        private Set<Float> avgs = new HashSet<>();
565
        private Set<Float> exacts = new HashSet<>();
566
        private Set<Float> maxs = new HashSet<>();
567
        private Set<Float> mins = new HashSet<>();
568
        private Set<Float> sampleSizes = new HashSet<>();
569
        private Set<Float> standardDevs = new HashSet<>();
570
        private Set<Float> lowerBounds = new HashSet<>();
571
        private Set<Float> upperBounds = new HashSet<>();
572
        private Set<Float> variances = new HashSet<>();
573

    
574
        public DescriptionElementCompareWrapper(DescriptionElementBase element) {
575
            this.element = element;
576
            if(element.isInstanceOf(CategoricalData.class)){
577
                CategoricalData elementData = (CategoricalData)element;
578
                elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
579
            }
580
            else if(element.isInstanceOf(QuantitativeData.class)){
581
                QuantitativeData elementData = (QuantitativeData)element;
582
                elementData.getStatisticalValues().forEach(value->{
583
                    if(value.getType().equals(StatisticalMeasure.AVERAGE())){
584
                        avgs.add(value.getValue());
585
                    }
586
                    else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
587
                        exacts.add(value.getValue());
588

    
589
                    }
590
                    else if(value.getType().equals(StatisticalMeasure.MAX())){
591
                        maxs.add(value.getValue());
592
                    }
593
                    else if(value.getType().equals(StatisticalMeasure.MIN())){
594
                        mins.add(value.getValue());
595
                    }
596
                    else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
597
                        sampleSizes.add(value.getValue());
598

    
599
                    }
600
                    else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
601
                        standardDevs.add(value.getValue());
602
                    }
603
                    else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
604
                        lowerBounds.add(value.getValue());
605

    
606
                    }
607
                    else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
608
                        upperBounds.add(value.getValue());
609
                    }
610
                    else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
611
                        variances.add(value.getValue());
612
                    }
613
                });
614
            }
615
        }
616

    
617
        @Override
618
        public int hashCode() {
619
            final int prime = 31;
620
            int result = 1;
621
            result = prime * result + getOuterType().hashCode();
622
            result = prime * result + ((avgs == null) ? 0 : avgs.hashCode());
623
            result = prime * result + ((element == null) ? 0 : element.hashCode());
624
            result = prime * result + ((exacts == null) ? 0 : exacts.hashCode());
625
            result = prime * result + ((lowerBounds == null) ? 0 : lowerBounds.hashCode());
626
            result = prime * result + ((maxs == null) ? 0 : maxs.hashCode());
627
            result = prime * result + ((mins == null) ? 0 : mins.hashCode());
628
            result = prime * result + ((sampleSizes == null) ? 0 : sampleSizes.hashCode());
629
            result = prime * result + ((standardDevs == null) ? 0 : standardDevs.hashCode());
630
            result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
631
            result = prime * result + ((upperBounds == null) ? 0 : upperBounds.hashCode());
632
            result = prime * result + ((variances == null) ? 0 : variances.hashCode());
633
            return result;
634
        }
635

    
636
        @Override
637
        public boolean equals(Object obj) {
638
            if (this == obj) {
639
                return true;
640
            }
641
            if (obj == null) {
642
                return false;
643
            }
644
            if (getClass() != obj.getClass()) {
645
                return false;
646
            }
647
            DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
648
            if (!getOuterType().equals(other.getOuterType())) {
649
                return false;
650
            }
651
            if (avgs == null) {
652
                if (other.avgs != null) {
653
                    return false;
654
                }
655
            } else if (!avgs.equals(other.avgs)) {
656
                return false;
657
            }
658
            if (element == null) {
659
                if (other.element != null) {
660
                    return false;
661
                }
662
            } else if (!element.equals(other.element)) {
663
                return false;
664
            }
665
            if (exacts == null) {
666
                if (other.exacts != null) {
667
                    return false;
668
                }
669
            } else if (!exacts.equals(other.exacts)) {
670
                return false;
671
            }
672
            if (lowerBounds == null) {
673
                if (other.lowerBounds != null) {
674
                    return false;
675
                }
676
            } else if (!lowerBounds.equals(other.lowerBounds)) {
677
                return false;
678
            }
679
            if (maxs == null) {
680
                if (other.maxs != null) {
681
                    return false;
682
                }
683
            } else if (!maxs.equals(other.maxs)) {
684
                return false;
685
            }
686
            if (mins == null) {
687
                if (other.mins != null) {
688
                    return false;
689
                }
690
            } else if (!mins.equals(other.mins)) {
691
                return false;
692
            }
693
            if (sampleSizes == null) {
694
                if (other.sampleSizes != null) {
695
                    return false;
696
                }
697
            } else if (!sampleSizes.equals(other.sampleSizes)) {
698
                return false;
699
            }
700
            if (standardDevs == null) {
701
                if (other.standardDevs != null) {
702
                    return false;
703
                }
704
            } else if (!standardDevs.equals(other.standardDevs)) {
705
                return false;
706
            }
707
            if (stateUuids == null) {
708
                if (other.stateUuids != null) {
709
                    return false;
710
                }
711
            } else if (!stateUuids.equals(other.stateUuids)) {
712
                return false;
713
            }
714
            if (upperBounds == null) {
715
                if (other.upperBounds != null) {
716
                    return false;
717
                }
718
            } else if (!upperBounds.equals(other.upperBounds)) {
719
                return false;
720
            }
721
            if (variances == null) {
722
                if (other.variances != null) {
723
                    return false;
724
                }
725
            } else if (!variances.equals(other.variances)) {
726
                return false;
727
            }
728
            return true;
729
        }
730

    
731
        private DescriptiveDataSetService getOuterType() {
732
            return DescriptiveDataSetService.this;
733
        }
734

    
735
    }
736

    
737
}
(17-17/103)