Project

General

Profile

Download (40.9 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.cdm.api.service;
2

    
3
import java.math.BigDecimal;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.Collection;
7
import java.util.Collections;
8
import java.util.HashMap;
9
import java.util.HashSet;
10
import java.util.List;
11
import java.util.Map;
12
import java.util.Optional;
13
import java.util.Set;
14
import java.util.UUID;
15
import java.util.stream.Collectors;
16

    
17
import org.apache.log4j.Logger;
18
import org.springframework.beans.factory.annotation.Autowired;
19
import org.springframework.stereotype.Service;
20
import org.springframework.transaction.annotation.Transactional;
21

    
22
import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
23
import eu.etaxonomy.cdm.api.service.config.DeleteDescriptiveDataSetConfigurator;
24
import eu.etaxonomy.cdm.api.service.config.IdentifiableServiceConfiguratorImpl;
25
import eu.etaxonomy.cdm.api.service.config.RemoveDescriptionsFromDescriptiveDataSetConfigurator;
26
import eu.etaxonomy.cdm.api.service.dto.DescriptionBaseDto;
27
import eu.etaxonomy.cdm.api.service.dto.RowWrapperDTO;
28
import eu.etaxonomy.cdm.api.service.dto.SpecimenRowWrapperDTO;
29
import eu.etaxonomy.cdm.api.service.dto.TaxonRowWrapperDTO;
30
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
31
import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
32
import eu.etaxonomy.cdm.format.description.DefaultCategoricalDescriptionBuilder;
33
import eu.etaxonomy.cdm.format.description.DefaultQuantitativeDescriptionBuilder;
34
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
35
import eu.etaxonomy.cdm.model.common.CdmBase;
36
import eu.etaxonomy.cdm.model.common.Language;
37
import eu.etaxonomy.cdm.model.description.CategoricalData;
38
import eu.etaxonomy.cdm.model.description.DescriptionBase;
39
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
40
import eu.etaxonomy.cdm.model.description.DescriptionType;
41
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
42
import eu.etaxonomy.cdm.model.description.DescriptiveSystemRole;
43
import eu.etaxonomy.cdm.model.description.Feature;
44
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
45
import eu.etaxonomy.cdm.model.description.PolytomousKey;
46
import eu.etaxonomy.cdm.model.description.QuantitativeData;
47
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
48
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
49
import eu.etaxonomy.cdm.model.description.TaxonDescription;
50
import eu.etaxonomy.cdm.model.description.TextData;
51
import eu.etaxonomy.cdm.model.location.NamedArea;
52
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
53
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
54
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
55
import eu.etaxonomy.cdm.model.reference.CdmLinkSource;
56
import eu.etaxonomy.cdm.model.taxon.Classification;
57
import eu.etaxonomy.cdm.model.taxon.Taxon;
58
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
59
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
60
import eu.etaxonomy.cdm.persistence.dao.description.IDescriptiveDataSetDao;
61
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
62
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
63
import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
64
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
65
import eu.etaxonomy.cdm.persistence.dto.TermDto;
66
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
67
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
68
import eu.etaxonomy.cdm.strategy.generate.PolytomousKeyGenerator;
69
import eu.etaxonomy.cdm.strategy.generate.PolytomousKeyGeneratorConfigurator;
70

    
71
@Service
72
@Transactional(readOnly=true)
73
public class DescriptiveDataSetService
74
        extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
75
        implements IDescriptiveDataSetService {
76

    
77
    private static Logger logger = Logger.getLogger(DescriptiveDataSetService.class);
78

    
79
    @Autowired
80
    private IOccurrenceService occurrenceService;
81

    
82
    @Autowired
83
    private ITaxonService taxonService;
84

    
85
    @Autowired
86
    private IPolytomousKeyService polytomousKeyService;
87

    
88
    @Autowired
89
    private IDefinedTermDao termDao;
90

    
91
    @Autowired
92
    private IDescriptionService descriptionService;
93

    
94
    @Autowired
95
    private ITaxonNodeService taxonNodeService;
96

    
97
	@Override
98
	@Autowired
99
	protected void setDao(IDescriptiveDataSetDao dao) {
100
		this.dao = dao;
101
	}
102

    
103
	@Override
104
    public Map<DescriptionBase, Set<DescriptionElementBase>> getDescriptionElements(DescriptiveDataSet descriptiveDataSet, Set<Feature> features, Integer pageSize,	Integer pageNumber,
105
			List<String> propertyPaths) {
106
		return dao.getDescriptionElements(descriptiveDataSet, features, pageSize, pageNumber, propertyPaths);
107
	}
108

    
109
	@Override
110
	public <T extends DescriptionElementBase> Map<UuidAndTitleCache, Map<UUID, Set<T>>> getTaxonFeatureDescriptionElementMap(
111
			Class<T> clazz, UUID descriptiveDataSetUuid, DescriptiveSystemRole role) {
112
		return dao.getTaxonFeatureDescriptionElementMap(clazz, descriptiveDataSetUuid, role);
113
	}
114

    
115
	@Override
116
    public List<UuidAndTitleCache<DescriptiveDataSet>> getDescriptiveDataSetUuidAndTitleCache(Integer limitOfInitialElements, String pattern) {
117
        return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
118
    }
119

    
120
	@Override
121
	public ArrayList<RowWrapperDTO> getRowWrapper(UUID descriptiveDataSetUuid, IProgressMonitor monitor) {
122
	    DescriptiveDataSet descriptiveDataSet = load(descriptiveDataSetUuid);
123
	    monitor.beginTask("Load row wrapper", descriptiveDataSet.getDescriptions().size());
124
	    ArrayList<RowWrapperDTO> wrappers = new ArrayList<>();
125
	    Set<DescriptionBase> descriptions = descriptiveDataSet.getDescriptions();
126
	    for (DescriptionBase description : descriptions) {
127
            if(monitor.isCanceled()){
128
                return new ArrayList<>();
129
            }
130
            RowWrapperDTO rowWrapper = null;
131
            // only viable descriptions are aggregated, literature or default descriptions
132
            if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class) &&
133
                    (description.isAggregatedStructuredDescription()
134
                            || description.getTypes().contains(DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION)
135
                            || description.getTypes().contains(DescriptionType.SECONDARY_DATA)
136
                            )){
137
                rowWrapper = createTaxonRowWrapper(description.getUuid(), descriptiveDataSet.getUuid());
138
            }
139
            else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)&&
140
                    !description.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE)){
141
                rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSetUuid);
142
            }
143
            if(rowWrapper!=null){
144
                wrappers.add(rowWrapper);
145
            }
146
            monitor.worked(1);
147
        }
148
	    return wrappers;
149
	}
150

    
151
    @Override
152
    public Collection<SpecimenNodeWrapper> loadSpecimens(DescriptiveDataSet descriptiveDataSet){
153
        List<UUID> filteredNodes = findFilteredTaxonNodes(descriptiveDataSet);
154
        if(filteredNodes.isEmpty()){
155
            return Collections.EMPTY_SET;
156
        }
157
        Collection<SpecimenNodeWrapper> result = occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
158

    
159
        return result;
160
    }
161

    
162
    @Override
163
    public Collection<SpecimenNodeWrapper> loadSpecimens(UUID descriptiveDataSetUuid){
164
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
165
        return loadSpecimens(dataSet);
166
    }
167

    
168

    
169
    @Override
170
    public List<UUID> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet){
171
        TaxonNodeFilter filter = TaxonNodeFilter.NewRankInstance(descriptiveDataSet.getMinRank(), descriptiveDataSet.getMaxRank());
172
        descriptiveDataSet.getGeoFilter().forEach(area -> filter.orArea(area.getUuid()));
173
        descriptiveDataSet.getTaxonSubtreeFilter().forEach(node -> filter.orSubtree(node));
174
        filter.setIncludeUnpublished(true);
175

    
176
        return taxonNodeService.uuidList(filter);
177
    }
178

    
179
    @Override
180
    public List<TaxonNode> loadFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet, List<String> propertyPaths){
181
        return taxonNodeService.load(findFilteredTaxonNodes(descriptiveDataSet), propertyPaths);
182
    }
183

    
184
    @Override
185
    public TaxonDescription findDefaultDescription(UUID specimenDescriptionUuid, UUID dataSetUuid){
186
        SpecimenDescription specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescriptionUuid);
187
        DescriptiveDataSet dataSet = load(dataSetUuid);
188
        TaxonNode node = findTaxonNodeForDescription(specimenDescription, dataSet);
189
        return recurseDefaultDescription(node, dataSet);
190
    }
191

    
192
    private TaxonDescription recurseDefaultDescription(TaxonNode node, DescriptiveDataSet dataSet){
193
        TaxonDescription defaultDescription = null;
194
        if(node!=null && node.getTaxon()!=null){
195
            defaultDescription = findTaxonDescriptionByDescriptionType(dataSet, node.getTaxon(), DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION);
196
            if(defaultDescription==null && node.getParent()!=null){
197
                defaultDescription = recurseDefaultDescription(node.getParent(), dataSet);
198
            }
199
        }
200
        return defaultDescription;
201
    }
202

    
203
    private TaxonNode findTaxonNodeForDescription(SpecimenDescription description, DescriptiveDataSet descriptiveDataSet){
204
        SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
205
        //get taxon node
206

    
207
        Set<IndividualsAssociation> associations = (Set<IndividualsAssociation>)descriptiveDataSet.getDescriptions()
208
                .stream()
209
                .flatMap(desc->desc.getElements().stream())// put all description element in one stream
210
                .filter(element->element instanceof IndividualsAssociation)
211
                .map(ia->(IndividualsAssociation)ia)
212
                .collect(Collectors.toSet());
213
        Classification classification = descriptiveDataSet.getTaxonSubtreeFilter().iterator().next().getClassification();
214
        for (IndividualsAssociation individualsAssociation : associations) {
215
            if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(specimen)){
216
                return ((TaxonDescription) individualsAssociation.getInDescription()).getTaxon().getTaxonNode(classification);
217
            }
218
        }
219
        return null;
220
    }
221

    
222
    @Override
223
    public TaxonRowWrapperDTO createTaxonRowWrapper(UUID taxonDescriptionUuid, UUID descriptiveDataSetUuid) {
224
        TaxonNode taxonNode = null;
225
        Classification classification = null;
226
        TaxonDescription description = (TaxonDescription) descriptionService.load(taxonDescriptionUuid,
227
                Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature"));
228
        DescriptiveDataSet descriptiveDataSet = dao.load(descriptiveDataSetUuid, null);
229
        Optional<TaxonNode> first = descriptiveDataSet.getTaxonSubtreeFilter().stream()
230
                .filter(node->node.getClassification()!=null).findFirst();
231
        Optional<Classification> classificationOptional = first.map(node->node.getClassification());
232
        Set<DescriptionBaseDto> descriptions = new HashSet<>();
233
        if(classificationOptional.isPresent()){
234
            classification = classificationOptional.get();
235
            Taxon taxon = (Taxon) taxonService.load(description.getTaxon().getId(), Arrays.asList("taxonNodes", "taxonNodes.classification"));
236
            taxonNode = taxon.getTaxonNode(classification);
237
            for (DescriptionBase desc: taxon.getDescriptions()){
238
                descriptions.add(new DescriptionBaseDto(desc));
239
            }
240
        }
241

    
242

    
243
        return new TaxonRowWrapperDTO(new DescriptionBaseDto(description), new TaxonNodeDto(taxonNode), descriptions);
244
    }
245

    
246
    @Override
247
    @Transactional(readOnly=false)
248
    public UpdateResult addRowWrapperToDataset(Collection<SpecimenRowWrapperDTO> wrappers, UUID datasetUuid){
249
        UpdateResult result = new UpdateResult();
250
        DescriptiveDataSet dataSet = load(datasetUuid);
251
        result.setCdmEntity(dataSet);
252

    
253
        List<UUID> taxonUuids = wrappers.stream().map(wrapper->wrapper.getTaxonNode().getTaxonUuid()).collect(Collectors.toList());
254
        List<TaxonBase> taxa = taxonService.load(taxonUuids, Arrays.asList(new String[]{"descriptions"}));
255

    
256
        for (SpecimenRowWrapperDTO wrapper : wrappers) {
257
            Optional<TaxonBase> findAny = taxa.stream().filter(taxon->taxon.getUuid().equals(wrapper.getTaxonNode().getTaxonUuid())).findAny();
258
            if(!findAny.isPresent()){
259
                result.addException(new IllegalArgumentException("Could not create wrapper for "+ wrapper.getSpecimen().getLabel()));
260
                continue;
261
            }
262
            Taxon taxon = (Taxon) findAny.get();
263
//            UUID taxonDescriptionUuid = wrapper..getTaxonDescriptionUuid();
264
            TaxonDescription taxonDescription = null;
265
//            if(taxonDescriptionUuid!=null){
266
//                taxonDescription = (TaxonDescription) descriptionService.load(taxonDescriptionUuid);
267
//            }
268
            SpecimenOrObservationBase specimen = occurrenceService.load(wrapper.getSpecimen().getUuid());
269
            if(taxonDescription==null){
270
                Optional<TaxonDescription> associationDescriptionOptional = taxon.getDescriptions().stream()
271
                        .filter(desc->desc.getTypes().contains(DescriptionType.INDIVIDUALS_ASSOCIATION))
272
                        .findFirst();
273
                if(!associationDescriptionOptional.isPresent()){
274
                    taxonDescription = TaxonDescription.NewInstance(taxon);
275
                }
276
                else{
277
                    taxonDescription = associationDescriptionOptional.get();
278
                }
279

    
280

    
281
                IndividualsAssociation association = IndividualsAssociation.NewInstance(specimen);
282
                taxonDescription.addElement(association);
283
                taxonService.saveOrUpdate(taxon);
284
                result.addUpdatedObject(taxon);
285
            }
286
            DescriptionBase specimenDescription = wrapper.getDescription().getDescription();
287
            if (specimenDescription.isPersited()){
288
                specimenDescription = descriptionService.load(specimenDescription.getUuid());
289
            }else {
290
                specimen.addDescription(specimenDescription);
291
            }
292
//            SpecimenRowWrapperDTO rowWrapper = createSpecimenRowWrapper(specimenDescription, wrapper.getTaxonNode().getUuid(), datasetUuid);
293
            if(wrapper==null){
294
                result.addException(new IllegalArgumentException("Could not create wrapper for "+wrapper.getDescription().getDescription()));
295
                continue;
296
            }
297
            //add specimen description to data set
298

    
299

    
300
//            specimenDescription = (SpecimenDescription) descriptionService.load(rowWrapper.getDescription().getDescription().getUuid());
301

    
302
            specimenDescription.addDescriptiveDataSet(dataSet);
303
            //add taxon description with IndividualsAssociation to the specimen to data set
304
            taxonDescription.addDescriptiveDataSet(dataSet);
305
            result.addUpdatedObject(specimen);
306
            result.addUpdatedObject(specimenDescription);
307
            result.addUpdatedObject(taxonDescription);
308
        }
309
        saveOrUpdate(dataSet);
310
        return result;
311
    }
312

    
313
    private SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID taxonNodeUuid,
314
            UUID datasetUuid) {
315
        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
316
        DescriptiveDataSet descriptiveDataSet = load(datasetUuid);
317
        SpecimenOrObservationBase specimen = HibernateProxyHelper.deproxy(description.getDescribedSpecimenOrObservation(), SpecimenOrObservationBase.class);
318
        //supplemental information
319
        if(taxonNode==null){
320
            taxonNode = findTaxonNodeForDescription(description, descriptiveDataSet);
321
        }
322
        FieldUnit fieldUnit = null;
323
        String identifier = null;
324
        NamedArea country = null;
325
        if(taxonNode==null){
326
            return null;
327
        }
328
        //taxon node was found
329

    
330
        //get field unit
331
        Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
332
                Arrays.asList(new String[]{
333
                        "gatheringEvent",
334
                        "gatheringEvent.country"
335
                }));
336
        if(fieldUnits.size()>1){
337
            logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
338
            return null;
339
        }
340
        else{
341
            if (fieldUnits != null && fieldUnits.size()>0){
342
                fieldUnit = fieldUnits.iterator().next();
343
            }
344
        }
345
        //get identifier
346
        if(HibernateProxyHelper.isInstanceOf(specimen, DerivedUnit.class)){
347
            identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
348
        }
349
        //get country
350
        if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
351
            country = fieldUnit.getGatheringEvent().getCountry();
352
        }
353
        //get default taxon description
354
//        TaxonDescription defaultTaxonDescription = findDefaultDescription(description.getUuid(), descriptiveDataSet.getUuid());
355
        TaxonDescription defaultTaxonDescription = recurseDefaultDescription(taxonNode, descriptiveDataSet);
356
        TaxonRowWrapperDTO taxonRowWrapper = defaultTaxonDescription != null
357
                ? createTaxonRowWrapper(defaultTaxonDescription.getUuid(), descriptiveDataSet.getUuid()) : null;
358
//                use description not specimen for specimenRow
359
        SpecimenRowWrapperDTO specimenRowWrapperDTO = new SpecimenRowWrapperDTO(new DescriptionBaseDto(description), specimen.getRecordBasis(), new TaxonNodeDto(taxonNode), fieldUnit, identifier, country);
360
        specimenRowWrapperDTO.setDefaultDescription(taxonRowWrapper);
361
        return specimenRowWrapperDTO;
362
    }
363

    
364
    @Override
365
    public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID descriptiveDataSetUuid){
366
        return createSpecimenRowWrapper(description, null, descriptiveDataSetUuid);
367
	}
368

    
369
    @Override
370
    public SpecimenRowWrapperDTO createSpecimenRowWrapper(UUID specimenUuid, UUID taxonNodeUuid, UUID descriptiveDataSetUuid){
371

    
372
        SpecimenOrObservationBase<?> specimen = occurrenceService.load(specimenUuid);
373
        SpecimenDescription specimenDescription = findSpecimenDescription(descriptiveDataSetUuid, specimen, true);
374
        return createSpecimenRowWrapper(specimenDescription, taxonNodeUuid, descriptiveDataSetUuid);
375
    }
376

    
377
    @Override
378
    @Transactional(readOnly = false)
379
    public UpdateResult updateCaches(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
380
            IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
381
        if (clazz == null) {
382
            clazz = DescriptiveDataSet.class;
383
        }
384
        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
385
    }
386

    
387
    private TaxonDescription findTaxonDescriptionByDescriptionType(DescriptiveDataSet dataSet, Taxon taxon, DescriptionType descriptionType){
388
        Optional<TaxonDescription> first = taxon.getDescriptions().stream()
389
                .filter(desc -> desc.getTypes().stream().anyMatch(type -> type.equals(descriptionType)))
390
                .filter(desc -> dataSet.getDescriptions().contains(desc))
391
                .findFirst();
392
        if(first.isPresent()){
393
            return HibernateProxyHelper.deproxy(descriptionService.load(first.get().getUuid(),
394
                  Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
395
        }
396
        return null;
397
    }
398

    
399
    @Override
400
    public TaxonDescription findTaxonDescriptionByDescriptionType(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
401
        DescriptiveDataSet dataSet = load(dataSetUuid);
402
        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
403
        return findTaxonDescriptionByDescriptionType(dataSet, taxonNode.getTaxon(), descriptionType);
404
    }
405

    
406
    @Override
407
    @Transactional(readOnly=false)
408
    public UpdateResult generatePolytomousKey(UUID descriptiveDataSetUuid, UUID taxonUuid) {
409
        UpdateResult result = new UpdateResult();
410

    
411
        PolytomousKeyGeneratorConfigurator keyConfig = new PolytomousKeyGeneratorConfigurator();
412
        DescriptiveDataSet descriptiveDataSet = load(descriptiveDataSetUuid);
413
        keyConfig.setDataSet(descriptiveDataSet);
414
        PolytomousKey key = new PolytomousKeyGenerator().invoke(keyConfig);
415
        IdentifiableServiceConfiguratorImpl<PolytomousKey> serviceConfig= new IdentifiableServiceConfiguratorImpl<>();
416
        serviceConfig.setTitleSearchString(descriptiveDataSet.getTitleCache());
417
        List<PolytomousKey> list = polytomousKeyService.findByTitle(serviceConfig).getRecords();
418
        if(list!=null){
419
            list.forEach(polytomousKey->polytomousKeyService.delete(polytomousKey));
420
        }
421
        key.setTitleCache(descriptiveDataSet.getTitleCache(), true);
422

    
423
        Taxon taxon = (Taxon) taxonService.load(taxonUuid);
424
        key.addTaxonomicScope(taxon);
425

    
426
        polytomousKeyService.saveOrUpdate(key);
427

    
428
        result.setCdmEntity(key);
429
        result.addUpdatedObject(taxon);
430
        return result;
431
    }
432

    
433
    @Override
434
    @Transactional(readOnly=false)
435
    public DeleteResult removeDescription(UUID descriptionUuid, UUID descriptiveDataSetUuid, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
436
        DeleteResult result = new DeleteResult();
437
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
438
        DescriptionBase descriptionBase = descriptionService.load(descriptionUuid);
439
        if(dataSet==null || descriptionBase==null){
440
            result.setError();
441
        }
442
        else{
443
            removeDescriptionFromDataSet(result, dataSet, descriptionBase, config);
444
        }
445
        return result;
446
    }
447

    
448

    
449
    @Override
450
    @Transactional(readOnly=false)
451
    public DeleteResult removeDescriptions(List<UUID> descriptionUuids, UUID descriptiveDataSetUuid, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
452
        DeleteResult result = new DeleteResult();
453
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
454
        List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
455
        if(dataSet==null || descriptions==null){
456
            result.setError();
457
        }
458
        else{
459
            for (DescriptionBase description: descriptions){
460
                removeDescriptionFromDataSet(result, dataSet, description, config);
461
            }
462

    
463

    
464
        }
465
        return result;
466
    }
467

    
468
    /**
469
     * @param result
470
     * @param dataSet
471
     * @param description
472
     */
473
    private void removeDescriptionFromDataSet(DeleteResult result, DescriptiveDataSet dataSet,
474
            DescriptionBase description, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
475
        if (description == null){
476
            return;
477
        }
478
        boolean success = dataSet.removeDescription(description);
479
        result.addDeletedObject(description);// remove taxon description with IndividualsAssociation from data set
480
        if(description instanceof SpecimenDescription){
481
            @SuppressWarnings("cast")
482
            Set<IndividualsAssociation> associations = (Set<IndividualsAssociation>)dataSet.getDescriptions()
483
                    .stream()
484
                    .flatMap(desc->desc.getElements().stream())// put all description element in one stream
485
                    .filter(element->element instanceof IndividualsAssociation)
486
                    .map(ia->(IndividualsAssociation)ia)
487
                    .collect(Collectors.toSet());
488
            Classification classification = dataSet.getTaxonSubtreeFilter().iterator().next().getClassification();
489
            for (IndividualsAssociation individualsAssociation : associations) {
490
                if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(description.getDescribedSpecimenOrObservation())){
491
                    dataSet.removeDescription(individualsAssociation.getInDescription());
492
                    result.addUpdatedObject(individualsAssociation.getInDescription());
493
                }
494
            }
495
        }
496
        if (description instanceof TaxonDescription){
497
            UUID taxonUuid = ((TaxonDescription)description).getTaxon().getUuid();
498
            DeleteResult isDeletable = descriptionService.isDeletable(description.getUuid());
499
            for (CdmBase relatedCdmBase: isDeletable.getRelatedObjects()){
500
                if (relatedCdmBase instanceof CdmLinkSource){
501
                    CdmLinkSource linkSource = (CdmLinkSource)relatedCdmBase;
502
                    if (linkSource.getTarget().equals(this)){
503

    
504
                    }
505
                }
506
            }
507

    
508

    
509
        }
510
        if (!config.isOnlyRemoveDescriptionsFromDataSet()){
511
            DeleteResult deleteResult = descriptionService.deleteDescription(description);
512
            result.includeResult(deleteResult);
513
            result.addUpdatedObject(dataSet);
514
        }else{
515
            MergeResult<DescriptiveDataSet> mergeResult = dao.merge(dataSet, true);
516
            result.addUpdatedObject(mergeResult.getMergedEntity());
517
        }
518

    
519
        result.setStatus(success?Status.OK:Status.ERROR);
520
    }
521

    
522
    @Override
523
    @Transactional(readOnly = false)
524
    public DeleteResult delete(UUID datasetUuid, DeleteDescriptiveDataSetConfigurator config,  IProgressMonitor monitor){
525
        DescriptiveDataSet dataSet = dao.load(datasetUuid);
526
        monitor.beginTask("Delete Descriptive Dataset", dataSet.getDescriptions().size() +1);
527

    
528
        DeleteResult result = new DeleteResult();
529
        DeleteResult descriptionResult = new DeleteResult();
530
        if (!dataSet.getDescriptions().isEmpty()){
531
            Set<DescriptionBase> descriptions = new HashSet();;
532
            for (DescriptionBase desc: dataSet.getDescriptions()){
533
                descriptions.add(desc);
534
            }
535
            monitor.subTask("Delete descriptions");
536
            for (DescriptionBase desc: descriptions){
537
                dataSet.removeDescription(desc);
538
                if (desc instanceof SpecimenDescription && config.isDeleteAllSpecimenDescriptions()){
539
                    descriptionResult.includeResult(descriptionService.deleteDescription(desc));
540
                }else if (desc instanceof TaxonDescription){
541
                    if( desc.getTypes().contains(DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION) && config.isDeleteAllDefaultDescriptions()){
542
                        descriptionResult.includeResult(descriptionService.deleteDescription(desc));
543
                    }else if (desc.getTypes().contains(DescriptionType.SECONDARY_DATA) && config.isDeleteAllLiteratureDescriptions()){
544
                        descriptionResult.includeResult(descriptionService.deleteDescription(desc));
545
                    }else if (desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC) && config.isDeleteAllAggregatedDescriptions()){
546
                        descriptionResult.includeResult(descriptionService.deleteDescription(desc));
547
                    }
548

    
549
                }
550

    
551
            }
552

    
553

    
554
        }
555
        UUID uuid = dao.delete(dataSet);
556
        monitor.worked(1);
557
        monitor.done();
558
        result.includeResult(descriptionResult);
559
        result.setStatus(Status.OK);
560
        result.addDeletedObject(dataSet);
561
        return result;
562
    }
563

    
564
    @Override
565
    @Transactional(readOnly=false)
566
    public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
567
        DescriptiveDataSet dataSet = load(dataSetUuid);
568
        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon"));
569
        TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
570
        newTaxonDescription.setTitleCache(dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
571
        newTaxonDescription.getTypes().add(descriptionType);
572

    
573
        dataSet.getDescriptiveSystem().getDistinctTerms().forEach(wsFeature->{
574
            if(wsFeature.isSupportsCategoricalData()){
575
                newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
576
            }
577
            else if(wsFeature.isSupportsQuantitativeData()){
578
                newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
579
            }
580
        });
581
        dataSet.addDescription(newTaxonDescription);
582
        saveOrUpdate(dataSet);
583
        return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
584
    }
585

    
586
    @Override
587
    public List<TermDto> getSupportedStatesForFeature(UUID featureUuid){
588
        return termDao.getSupportedStatesForFeature(featureUuid);
589
    }
590

    
591
    @Override
592
    @Transactional(readOnly=false)
593
    public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, SpecimenOrObservationBase specimen, boolean addDatasetSource){
594
        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
595
//        SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
596

    
597
        Set<? extends Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctTerms();
598
        List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
599

    
600
        for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
601
            specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
602

    
603
            //check if description is already added to data set
604
            if(dataSet.getDescriptions().contains(specimenDescription) ){
605
                return specimenDescription;
606
            }
607

    
608
            //gather specimen description features and check for match with dataset features
609
            Set<Feature> specimenDescriptionFeatures = new HashSet<>();
610
            for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
611
                Feature feature = specimenDescriptionElement.getFeature();
612
                specimenDescriptionFeatures.add(feature);
613
                if(datasetFeatures.contains(feature) && RowWrapperDTO.hasData(specimenDescriptionElement)){
614
                    matchingDescriptionElements.add(specimenDescriptionElement);
615
                }
616
            }
617
        }
618
        //Create new specimen description if description has not already been added to the dataset
619
        SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
620
        newDesription.setTitleCache("Dataset "+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
621

    
622
        //check for equals description element (same feature and same values)
623
        Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
624
        for(DescriptionElementBase element:matchingDescriptionElements){
625
            List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
626
            if(list==null){
627
                list = new ArrayList<>();
628
            }
629
            list.add(element);
630
            featureToElementMap.put(element.getFeature(), list);
631
        }
632
        Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
633
        for(Feature feature:featureToElementMap.keySet()){
634
            List<DescriptionElementBase> elements = featureToElementMap.get(feature);
635
            //no duplicate description elements found for this feature
636
            if(elements.size()==1){
637
                descriptionElementsToClone.add(elements.get(0));
638
            }
639
            //duplicates found -> check if all are equal
640
            else{
641
                DescriptionElementBase match = null;
642
                for (DescriptionElementBase descriptionElementBase : elements) {
643
                    if(match==null){
644
                        match = descriptionElementBase;
645
                    }
646
                    else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
647
                        match = null;
648
                        //TODO: propagate message
649
//                        MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
650
//                                String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
651
                        break;
652
                    }
653
                }
654
                if(match!=null){
655
                    descriptionElementsToClone.add(match);
656
                }
657
            }
658
        }
659
        //clone matching descriptionElements
660
        for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
661
            DescriptionElementBase clone;
662
            try {
663
                clone = descriptionElementBase.clone(newDesription);
664
                clone.getSources().forEach(source -> {
665
                    if(descriptionElementBase instanceof CategoricalData){
666
                        TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, Arrays.asList(new Language[]{Language.DEFAULT()}));
667
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
668
                    }
669
                    else if(descriptionElementBase instanceof QuantitativeData){
670
                        TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, Arrays.asList(new Language[]{Language.DEFAULT()}));
671
                        source.setOriginalNameString(label.getText(Language.DEFAULT()));
672
                    }
673
                });
674
            } catch (CloneNotSupportedException e) {
675
                //nothing
676
            }
677
        }
678

    
679
        //add sources of data set
680
        if(addDatasetSource){
681
            dataSet.getSources().forEach(source->{
682
                try {
683
                    newDesription.addSource(source.clone());
684
                } catch (CloneNotSupportedException e) {
685
                    //nothing
686
                }
687
            });
688
        }
689
        return newDesription;
690

    
691
    }
692

    
693
    //TODO: this should either be solved in the model class itself
694
    //OR this should cover all possibilities including modifiers for example
695
    private class DescriptionElementCompareWrapper {
696

    
697
        private DescriptionElementBase element;
698
        private Set<UUID> stateUuids = new HashSet<>();
699
        private Set<BigDecimal> avgs = new HashSet<>();
700
        private Set<BigDecimal> exacts = new HashSet<>();
701
        private Set<BigDecimal> maxs = new HashSet<>();
702
        private Set<BigDecimal> mins = new HashSet<>();
703
        private Set<BigDecimal> sampleSizes = new HashSet<>();
704
        private Set<BigDecimal> standardDevs = new HashSet<>();
705
        private Set<BigDecimal> lowerBounds = new HashSet<>();
706
        private Set<BigDecimal> upperBounds = new HashSet<>();
707
        private Set<BigDecimal> variances = new HashSet<>();
708

    
709
        public DescriptionElementCompareWrapper(DescriptionElementBase element) {
710
            this.element = element;
711
            if(element.isInstanceOf(CategoricalData.class)){
712
                CategoricalData elementData = (CategoricalData)element;
713
                elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
714
            }
715
            else if(element.isInstanceOf(QuantitativeData.class)){
716
                QuantitativeData elementData = (QuantitativeData)element;
717
                elementData.getStatisticalValues().forEach(value->{
718
                    if(value.getType().equals(StatisticalMeasure.AVERAGE())){
719
                        avgs.add(value.getValue());
720
                    }
721
                    else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
722
                        exacts.add(value.getValue());
723

    
724
                    }
725
                    else if(value.getType().equals(StatisticalMeasure.MAX())){
726
                        maxs.add(value.getValue());
727
                    }
728
                    else if(value.getType().equals(StatisticalMeasure.MIN())){
729
                        mins.add(value.getValue());
730
                    }
731
                    else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
732
                        sampleSizes.add(value.getValue());
733

    
734
                    }
735
                    else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
736
                        standardDevs.add(value.getValue());
737
                    }
738
                    else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
739
                        lowerBounds.add(value.getValue());
740

    
741
                    }
742
                    else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
743
                        upperBounds.add(value.getValue());
744
                    }
745
                    else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
746
                        variances.add(value.getValue());
747
                    }
748
                });
749
            }
750
        }
751

    
752
        @Override
753
        public int hashCode() {
754
            final int prime = 31;
755
            int result = 1;
756
            result = prime * result + getOuterType().hashCode();
757
            result = prime * result + ((avgs == null) ? 0 : avgs.hashCode());
758
            result = prime * result + ((element == null) ? 0 : element.hashCode());
759
            result = prime * result + ((exacts == null) ? 0 : exacts.hashCode());
760
            result = prime * result + ((lowerBounds == null) ? 0 : lowerBounds.hashCode());
761
            result = prime * result + ((maxs == null) ? 0 : maxs.hashCode());
762
            result = prime * result + ((mins == null) ? 0 : mins.hashCode());
763
            result = prime * result + ((sampleSizes == null) ? 0 : sampleSizes.hashCode());
764
            result = prime * result + ((standardDevs == null) ? 0 : standardDevs.hashCode());
765
            result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
766
            result = prime * result + ((upperBounds == null) ? 0 : upperBounds.hashCode());
767
            result = prime * result + ((variances == null) ? 0 : variances.hashCode());
768
            return result;
769
        }
770

    
771
        @Override
772
        public boolean equals(Object obj) {
773
            if (this == obj) {
774
                return true;
775
            }
776
            if (obj == null) {
777
                return false;
778
            }
779
            if (getClass() != obj.getClass()) {
780
                return false;
781
            }
782
            DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
783
            if (!getOuterType().equals(other.getOuterType())) {
784
                return false;
785
            }
786
            if (avgs == null) {
787
                if (other.avgs != null) {
788
                    return false;
789
                }
790
            } else if (!avgs.equals(other.avgs)) {
791
                return false;
792
            }
793
            if (element == null) {
794
                if (other.element != null) {
795
                    return false;
796
                }
797
            } else if (!element.equals(other.element)) {
798
                return false;
799
            }
800
            if (exacts == null) {
801
                if (other.exacts != null) {
802
                    return false;
803
                }
804
            } else if (!exacts.equals(other.exacts)) {
805
                return false;
806
            }
807
            if (lowerBounds == null) {
808
                if (other.lowerBounds != null) {
809
                    return false;
810
                }
811
            } else if (!lowerBounds.equals(other.lowerBounds)) {
812
                return false;
813
            }
814
            if (maxs == null) {
815
                if (other.maxs != null) {
816
                    return false;
817
                }
818
            } else if (!maxs.equals(other.maxs)) {
819
                return false;
820
            }
821
            if (mins == null) {
822
                if (other.mins != null) {
823
                    return false;
824
                }
825
            } else if (!mins.equals(other.mins)) {
826
                return false;
827
            }
828
            if (sampleSizes == null) {
829
                if (other.sampleSizes != null) {
830
                    return false;
831
                }
832
            } else if (!sampleSizes.equals(other.sampleSizes)) {
833
                return false;
834
            }
835
            if (standardDevs == null) {
836
                if (other.standardDevs != null) {
837
                    return false;
838
                }
839
            } else if (!standardDevs.equals(other.standardDevs)) {
840
                return false;
841
            }
842
            if (stateUuids == null) {
843
                if (other.stateUuids != null) {
844
                    return false;
845
                }
846
            } else if (!stateUuids.equals(other.stateUuids)) {
847
                return false;
848
            }
849
            if (upperBounds == null) {
850
                if (other.upperBounds != null) {
851
                    return false;
852
                }
853
            } else if (!upperBounds.equals(other.upperBounds)) {
854
                return false;
855
            }
856
            if (variances == null) {
857
                if (other.variances != null) {
858
                    return false;
859
                }
860
            } else if (!variances.equals(other.variances)) {
861
                return false;
862
            }
863
            return true;
864
        }
865

    
866
        private DescriptiveDataSetService getOuterType() {
867
            return DescriptiveDataSetService.this;
868
        }
869

    
870

    
871

    
872
    }
873

    
874
}
(12-12/100)