Project

General

Profile

Download (34.2 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.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
}
(17-17/103)