ed01c538f64c9cadb7b964a153ffbf84eaaaf47f
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / DescriptiveDataSetService.java
1 package eu.etaxonomy.cdm.api.service;
2
3 import java.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 taxonDescription.addType(DescriptionType.INDIVIDUALS_ASSOCIATION);
276 }
277 else{
278 taxonDescription = associationDescriptionOptional.get();
279 }
280
281
282 IndividualsAssociation association = IndividualsAssociation.NewInstance(specimen);
283 taxonDescription.addElement(association);
284 taxonService.saveOrUpdate(taxon);
285 result.addUpdatedObject(taxon);
286 }
287 DescriptionBase specimenDescription = wrapper.getDescription().getDescription();
288 if (specimenDescription.isPersited()){
289 specimenDescription = descriptionService.load(specimenDescription.getUuid());
290 }else {
291 specimen.addDescription(specimenDescription);
292 }
293 // SpecimenRowWrapperDTO rowWrapper = createSpecimenRowWrapper(specimenDescription, wrapper.getTaxonNode().getUuid(), datasetUuid);
294 if(wrapper==null){
295 result.addException(new IllegalArgumentException("Could not create wrapper for "+wrapper.getDescription().getDescription()));
296 continue;
297 }
298 //add specimen description to data set
299
300
301 // specimenDescription = (SpecimenDescription) descriptionService.load(rowWrapper.getDescription().getDescription().getUuid());
302
303 specimenDescription.addDescriptiveDataSet(dataSet);
304 //add taxon description with IndividualsAssociation to the specimen to data set
305 taxonDescription.addDescriptiveDataSet(dataSet);
306 result.addUpdatedObject(specimen);
307 result.addUpdatedObject(specimenDescription);
308 result.addUpdatedObject(taxonDescription);
309 }
310 saveOrUpdate(dataSet);
311 return result;
312 }
313
314 private SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID taxonNodeUuid,
315 UUID datasetUuid) {
316 TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
317 DescriptiveDataSet descriptiveDataSet = load(datasetUuid);
318 SpecimenOrObservationBase specimen = HibernateProxyHelper.deproxy(description.getDescribedSpecimenOrObservation(), SpecimenOrObservationBase.class);
319 //supplemental information
320 if(taxonNode==null){
321 taxonNode = findTaxonNodeForDescription(description, descriptiveDataSet);
322 }
323 FieldUnit fieldUnit = null;
324 String identifier = null;
325 NamedArea country = null;
326 if(taxonNode==null){
327 return null;
328 }
329 //taxon node was found
330
331 //get field unit
332 Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(specimen.getUuid(),
333 Arrays.asList(new String[]{
334 "gatheringEvent",
335 "gatheringEvent.country"
336 }));
337 if(fieldUnits.size()>1){
338 logger.error("More than one or no field unit found for specimen"); //$NON-NLS-1$
339 return null;
340 }
341 else{
342 if (fieldUnits != null && fieldUnits.size()>0){
343 fieldUnit = fieldUnits.iterator().next();
344 }
345 }
346 //get identifier
347 if(HibernateProxyHelper.isInstanceOf(specimen, DerivedUnit.class)){
348 identifier = occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
349 }
350 //get country
351 if(fieldUnit!=null && fieldUnit.getGatheringEvent()!=null){
352 country = fieldUnit.getGatheringEvent().getCountry();
353 }
354 //get default taxon description
355 // TaxonDescription defaultTaxonDescription = findDefaultDescription(description.getUuid(), descriptiveDataSet.getUuid());
356 TaxonDescription defaultTaxonDescription = recurseDefaultDescription(taxonNode, descriptiveDataSet);
357 TaxonRowWrapperDTO taxonRowWrapper = defaultTaxonDescription != null
358 ? createTaxonRowWrapper(defaultTaxonDescription.getUuid(), descriptiveDataSet.getUuid()) : null;
359 // use description not specimen for specimenRow
360 SpecimenRowWrapperDTO specimenRowWrapperDTO = new SpecimenRowWrapperDTO(new DescriptionBaseDto(description), specimen.getRecordBasis(), new TaxonNodeDto(taxonNode), fieldUnit, identifier, country);
361 specimenRowWrapperDTO.setDefaultDescription(taxonRowWrapper);
362 return specimenRowWrapperDTO;
363 }
364
365 @Override
366 public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID descriptiveDataSetUuid){
367 return createSpecimenRowWrapper(description, null, descriptiveDataSetUuid);
368 }
369
370 @Override
371 public SpecimenRowWrapperDTO createSpecimenRowWrapper(UUID specimenUuid, UUID taxonNodeUuid, UUID descriptiveDataSetUuid){
372
373 SpecimenOrObservationBase<?> specimen = occurrenceService.load(specimenUuid);
374 SpecimenDescription specimenDescription = findSpecimenDescription(descriptiveDataSetUuid, specimen, true);
375 return createSpecimenRowWrapper(specimenDescription, taxonNodeUuid, descriptiveDataSetUuid);
376 }
377
378 @Override
379 @Transactional(readOnly = false)
380 public UpdateResult updateCaches(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
381 IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
382 if (clazz == null) {
383 clazz = DescriptiveDataSet.class;
384 }
385 return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
386 }
387
388 private TaxonDescription findTaxonDescriptionByDescriptionType(DescriptiveDataSet dataSet, Taxon taxon, DescriptionType descriptionType){
389 Optional<TaxonDescription> first = taxon.getDescriptions().stream()
390 .filter(desc -> desc.getTypes().stream().anyMatch(type -> type.equals(descriptionType)))
391 .filter(desc -> dataSet.getDescriptions().contains(desc))
392 .findFirst();
393 if(first.isPresent()){
394 return HibernateProxyHelper.deproxy(descriptionService.load(first.get().getUuid(),
395 Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
396 }
397 return null;
398 }
399
400 @Override
401 public TaxonDescription findTaxonDescriptionByDescriptionType(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
402 DescriptiveDataSet dataSet = load(dataSetUuid);
403 TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
404 return findTaxonDescriptionByDescriptionType(dataSet, taxonNode.getTaxon(), descriptionType);
405 }
406
407 @Override
408 @Transactional(readOnly=false)
409 public UpdateResult generatePolytomousKey(UUID descriptiveDataSetUuid, UUID taxonUuid) {
410 UpdateResult result = new UpdateResult();
411
412 PolytomousKeyGeneratorConfigurator keyConfig = new PolytomousKeyGeneratorConfigurator();
413 DescriptiveDataSet descriptiveDataSet = load(descriptiveDataSetUuid);
414 keyConfig.setDataSet(descriptiveDataSet);
415 PolytomousKey key = new PolytomousKeyGenerator().invoke(keyConfig);
416 IdentifiableServiceConfiguratorImpl<PolytomousKey> serviceConfig= new IdentifiableServiceConfiguratorImpl<>();
417 serviceConfig.setTitleSearchString(descriptiveDataSet.getTitleCache());
418 List<PolytomousKey> list = polytomousKeyService.findByTitle(serviceConfig).getRecords();
419 if(list!=null){
420 list.forEach(polytomousKey->polytomousKeyService.delete(polytomousKey));
421 }
422 key.setTitleCache(descriptiveDataSet.getTitleCache(), true);
423
424 Taxon taxon = (Taxon) taxonService.load(taxonUuid);
425 key.addTaxonomicScope(taxon);
426
427 polytomousKeyService.saveOrUpdate(key);
428
429 result.setCdmEntity(key);
430 result.addUpdatedObject(taxon);
431 return result;
432 }
433
434 @Override
435 @Transactional(readOnly=false)
436 public DeleteResult removeDescription(UUID descriptionUuid, UUID descriptiveDataSetUuid, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
437 DeleteResult result = new DeleteResult();
438 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
439 DescriptionBase descriptionBase = descriptionService.load(descriptionUuid);
440 if(dataSet==null || descriptionBase==null){
441 result.setError();
442 }
443 else{
444 removeDescriptionFromDataSet(result, dataSet, descriptionBase, config);
445 }
446 return result;
447 }
448
449
450 @Override
451 @Transactional(readOnly=false)
452 public DeleteResult removeDescriptions(List<UUID> descriptionUuids, UUID descriptiveDataSetUuid, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
453 DeleteResult result = new DeleteResult();
454 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
455 List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
456 if(dataSet==null || descriptions==null){
457 result.setError();
458 }
459 else{
460 for (DescriptionBase description: descriptions){
461 removeDescriptionFromDataSet(result, dataSet, description, config);
462 }
463
464
465 }
466 return result;
467 }
468
469 /**
470 * @param result
471 * @param dataSet
472 * @param description
473 */
474 private void removeDescriptionFromDataSet(DeleteResult result, DescriptiveDataSet dataSet,
475 DescriptionBase description, RemoveDescriptionsFromDescriptiveDataSetConfigurator config) {
476 if (description == null){
477 return;
478 }
479 boolean success = dataSet.removeDescription(description);
480 result.addDeletedObject(description);// remove taxon description with IndividualsAssociation from data set
481 if(description instanceof SpecimenDescription){
482 @SuppressWarnings("cast")
483 Set<IndividualsAssociation> associations = (Set<IndividualsAssociation>)dataSet.getDescriptions()
484 .stream()
485 .flatMap(desc->desc.getElements().stream())// put all description element in one stream
486 .filter(element->element instanceof IndividualsAssociation)
487 .map(ia->(IndividualsAssociation)ia)
488 .collect(Collectors.toSet());
489 Classification classification = dataSet.getTaxonSubtreeFilter().iterator().next().getClassification();
490 for (IndividualsAssociation individualsAssociation : associations) {
491 if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(description.getDescribedSpecimenOrObservation())){
492 dataSet.removeDescription(individualsAssociation.getInDescription());
493 result.addUpdatedObject(individualsAssociation.getInDescription());
494 }
495 }
496 }
497 if (description instanceof TaxonDescription){
498 UUID taxonUuid = ((TaxonDescription)description).getTaxon().getUuid();
499 DeleteResult isDeletable = descriptionService.isDeletable(description.getUuid());
500 for (CdmBase relatedCdmBase: isDeletable.getRelatedObjects()){
501 if (relatedCdmBase instanceof CdmLinkSource){
502 CdmLinkSource linkSource = (CdmLinkSource)relatedCdmBase;
503 if (linkSource.getTarget().equals(this)){
504
505 }
506 }
507 }
508
509
510 }
511 if (!config.isOnlyRemoveDescriptionsFromDataSet()){
512 DeleteResult deleteResult = descriptionService.deleteDescription(description);
513 result.includeResult(deleteResult);
514 result.addUpdatedObject(dataSet);
515 }else{
516 MergeResult<DescriptiveDataSet> mergeResult = dao.merge(dataSet, true);
517 result.addUpdatedObject(mergeResult.getMergedEntity());
518 }
519
520 result.setStatus(success?Status.OK:Status.ERROR);
521 }
522
523 @Override
524 @Transactional(readOnly = false)
525 public DeleteResult delete(UUID datasetUuid, DeleteDescriptiveDataSetConfigurator config, IProgressMonitor monitor){
526 DescriptiveDataSet dataSet = dao.load(datasetUuid);
527 monitor.beginTask("Delete Descriptive Dataset", dataSet.getDescriptions().size() +1);
528
529 DeleteResult result = new DeleteResult();
530 DeleteResult descriptionResult = new DeleteResult();
531 if (!dataSet.getDescriptions().isEmpty()){
532 Set<DescriptionBase> descriptions = new HashSet();;
533 for (DescriptionBase desc: dataSet.getDescriptions()){
534 descriptions.add(desc);
535 }
536 monitor.subTask("Delete descriptions");
537 for (DescriptionBase desc: descriptions){
538 dataSet.removeDescription(desc);
539 if (desc instanceof SpecimenDescription && config.isDeleteAllSpecimenDescriptions()){
540 descriptionResult.includeResult(descriptionService.deleteDescription(desc));
541 }else if (desc instanceof TaxonDescription){
542 if( desc.getTypes().contains(DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION) && config.isDeleteAllDefaultDescriptions()){
543 descriptionResult.includeResult(descriptionService.deleteDescription(desc));
544 }else if (desc.getTypes().contains(DescriptionType.SECONDARY_DATA) && config.isDeleteAllLiteratureDescriptions()){
545 descriptionResult.includeResult(descriptionService.deleteDescription(desc));
546 }else if (desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC) && config.isDeleteAllAggregatedDescriptions()){
547 descriptionResult.includeResult(descriptionService.deleteDescription(desc));
548 }
549
550 }
551
552 }
553
554
555 }
556 UUID uuid = dao.delete(dataSet);
557 monitor.worked(1);
558 monitor.done();
559 result.includeResult(descriptionResult);
560 result.setStatus(Status.OK);
561 result.addDeletedObject(dataSet);
562 return result;
563 }
564
565 @Override
566 @Transactional(readOnly=false)
567 public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
568 DescriptiveDataSet dataSet = load(dataSetUuid);
569 TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon"));
570 TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
571 newTaxonDescription.setTitleCache(dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
572 newTaxonDescription.getTypes().add(descriptionType);
573
574 dataSet.getDescriptiveSystem().getDistinctTerms().forEach(wsFeature->{
575 if(wsFeature.isSupportsCategoricalData()){
576 newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
577 }
578 else if(wsFeature.isSupportsQuantitativeData()){
579 newTaxonDescription.addElement(QuantitativeData.NewInstance(wsFeature));
580 }
581 });
582 dataSet.addDescription(newTaxonDescription);
583 saveOrUpdate(dataSet);
584 return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
585 }
586
587 @Override
588 public List<TermDto> getSupportedStatesForFeature(UUID featureUuid){
589 return termDao.getSupportedStatesForFeature(featureUuid);
590 }
591
592 @Override
593 @Transactional(readOnly=false)
594 public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, SpecimenOrObservationBase specimen, boolean addDatasetSource){
595 DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
596 // SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
597
598 Set<? extends Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctTerms();
599 List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
600
601 for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
602 specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescription.getUuid());
603
604 //check if description is already added to data set
605 if(dataSet.getDescriptions().contains(specimenDescription) ){
606 return specimenDescription;
607 }
608
609 //gather specimen description features and check for match with dataset features
610 Set<Feature> specimenDescriptionFeatures = new HashSet<>();
611 for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
612 Feature feature = specimenDescriptionElement.getFeature();
613 specimenDescriptionFeatures.add(feature);
614 if(datasetFeatures.contains(feature) && RowWrapperDTO.hasData(specimenDescriptionElement)){
615 matchingDescriptionElements.add(specimenDescriptionElement);
616 }
617 }
618 }
619 //Create new specimen description if description has not already been added to the dataset
620 SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
621 newDesription.setTitleCache("Dataset "+dataSet.getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
622
623 //check for equals description element (same feature and same values)
624 Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
625 for(DescriptionElementBase element:matchingDescriptionElements){
626 List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
627 if(list==null){
628 list = new ArrayList<>();
629 }
630 list.add(element);
631 featureToElementMap.put(element.getFeature(), list);
632 }
633 Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
634 for(Feature feature:featureToElementMap.keySet()){
635 List<DescriptionElementBase> elements = featureToElementMap.get(feature);
636 //no duplicate description elements found for this feature
637 if(elements.size()==1){
638 descriptionElementsToClone.add(elements.get(0));
639 }
640 //duplicates found -> check if all are equal
641 else{
642 DescriptionElementBase match = null;
643 for (DescriptionElementBase descriptionElementBase : elements) {
644 if(match==null){
645 match = descriptionElementBase;
646 }
647 else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
648 match = null;
649 //TODO: propagate message
650 // MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
651 // String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
652 break;
653 }
654 }
655 if(match!=null){
656 descriptionElementsToClone.add(match);
657 }
658 }
659 }
660 //clone matching descriptionElements
661 for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
662 DescriptionElementBase clone;
663 try {
664 clone = descriptionElementBase.clone(newDesription);
665 clone.getSources().forEach(source -> {
666 if(descriptionElementBase instanceof CategoricalData){
667 TextData label = new DefaultCategoricalDescriptionBuilder().build((CategoricalData) descriptionElementBase, Arrays.asList(new Language[]{Language.DEFAULT()}));
668 source.setOriginalNameString(label.getText(Language.DEFAULT()));
669 }
670 else if(descriptionElementBase instanceof QuantitativeData){
671 TextData label = new DefaultQuantitativeDescriptionBuilder().build((QuantitativeData) descriptionElementBase, Arrays.asList(new Language[]{Language.DEFAULT()}));
672 source.setOriginalNameString(label.getText(Language.DEFAULT()));
673 }
674 });
675 } catch (CloneNotSupportedException e) {
676 //nothing
677 }
678 }
679
680 //add sources of data set
681 if(addDatasetSource){
682 dataSet.getSources().forEach(source->{
683 try {
684 newDesription.addSource(source.clone());
685 } catch (CloneNotSupportedException e) {
686 //nothing
687 }
688 });
689 }
690 return newDesription;
691
692 }
693
694 //TODO: this should either be solved in the model class itself
695 //OR this should cover all possibilities including modifiers for example
696 private class DescriptionElementCompareWrapper {
697
698 private DescriptionElementBase element;
699 private Set<UUID> stateUuids = new HashSet<>();
700 private Set<BigDecimal> avgs = new HashSet<>();
701 private Set<BigDecimal> exacts = new HashSet<>();
702 private Set<BigDecimal> maxs = new HashSet<>();
703 private Set<BigDecimal> mins = new HashSet<>();
704 private Set<BigDecimal> sampleSizes = new HashSet<>();
705 private Set<BigDecimal> standardDevs = new HashSet<>();
706 private Set<BigDecimal> lowerBounds = new HashSet<>();
707 private Set<BigDecimal> upperBounds = new HashSet<>();
708 private Set<BigDecimal> variances = new HashSet<>();
709
710 public DescriptionElementCompareWrapper(DescriptionElementBase element) {
711 this.element = element;
712 if(element.isInstanceOf(CategoricalData.class)){
713 CategoricalData elementData = (CategoricalData)element;
714 elementData.getStatesOnly().forEach(state->stateUuids.add(state.getUuid()));
715 }
716 else if(element.isInstanceOf(QuantitativeData.class)){
717 QuantitativeData elementData = (QuantitativeData)element;
718 elementData.getStatisticalValues().forEach(value->{
719 if(value.getType().equals(StatisticalMeasure.AVERAGE())){
720 avgs.add(value.getValue());
721 }
722 else if(value.getType().equals(StatisticalMeasure.EXACT_VALUE())){
723 exacts.add(value.getValue());
724
725 }
726 else if(value.getType().equals(StatisticalMeasure.MAX())){
727 maxs.add(value.getValue());
728 }
729 else if(value.getType().equals(StatisticalMeasure.MIN())){
730 mins.add(value.getValue());
731 }
732 else if(value.getType().equals(StatisticalMeasure.SAMPLE_SIZE())){
733 sampleSizes.add(value.getValue());
734
735 }
736 else if(value.getType().equals(StatisticalMeasure.STANDARD_DEVIATION())){
737 standardDevs.add(value.getValue());
738 }
739 else if(value.getType().equals(StatisticalMeasure.TYPICAL_LOWER_BOUNDARY())){
740 lowerBounds.add(value.getValue());
741
742 }
743 else if(value.getType().equals(StatisticalMeasure.TYPICAL_UPPER_BOUNDARY())){
744 upperBounds.add(value.getValue());
745 }
746 else if(value.getType().equals(StatisticalMeasure.VARIANCE())){
747 variances.add(value.getValue());
748 }
749 });
750 }
751 }
752
753 @Override
754 public int hashCode() {
755 final int prime = 31;
756 int result = 1;
757 result = prime * result + getOuterType().hashCode();
758 result = prime * result + ((avgs == null) ? 0 : avgs.hashCode());
759 result = prime * result + ((element == null) ? 0 : element.hashCode());
760 result = prime * result + ((exacts == null) ? 0 : exacts.hashCode());
761 result = prime * result + ((lowerBounds == null) ? 0 : lowerBounds.hashCode());
762 result = prime * result + ((maxs == null) ? 0 : maxs.hashCode());
763 result = prime * result + ((mins == null) ? 0 : mins.hashCode());
764 result = prime * result + ((sampleSizes == null) ? 0 : sampleSizes.hashCode());
765 result = prime * result + ((standardDevs == null) ? 0 : standardDevs.hashCode());
766 result = prime * result + ((stateUuids == null) ? 0 : stateUuids.hashCode());
767 result = prime * result + ((upperBounds == null) ? 0 : upperBounds.hashCode());
768 result = prime * result + ((variances == null) ? 0 : variances.hashCode());
769 return result;
770 }
771
772 @Override
773 public boolean equals(Object obj) {
774 if (this == obj) {
775 return true;
776 }
777 if (obj == null) {
778 return false;
779 }
780 if (getClass() != obj.getClass()) {
781 return false;
782 }
783 DescriptionElementCompareWrapper other = (DescriptionElementCompareWrapper) obj;
784 if (!getOuterType().equals(other.getOuterType())) {
785 return false;
786 }
787 if (avgs == null) {
788 if (other.avgs != null) {
789 return false;
790 }
791 } else if (!avgs.equals(other.avgs)) {
792 return false;
793 }
794 if (element == null) {
795 if (other.element != null) {
796 return false;
797 }
798 } else if (!element.equals(other.element)) {
799 return false;
800 }
801 if (exacts == null) {
802 if (other.exacts != null) {
803 return false;
804 }
805 } else if (!exacts.equals(other.exacts)) {
806 return false;
807 }
808 if (lowerBounds == null) {
809 if (other.lowerBounds != null) {
810 return false;
811 }
812 } else if (!lowerBounds.equals(other.lowerBounds)) {
813 return false;
814 }
815 if (maxs == null) {
816 if (other.maxs != null) {
817 return false;
818 }
819 } else if (!maxs.equals(other.maxs)) {
820 return false;
821 }
822 if (mins == null) {
823 if (other.mins != null) {
824 return false;
825 }
826 } else if (!mins.equals(other.mins)) {
827 return false;
828 }
829 if (sampleSizes == null) {
830 if (other.sampleSizes != null) {
831 return false;
832 }
833 } else if (!sampleSizes.equals(other.sampleSizes)) {
834 return false;
835 }
836 if (standardDevs == null) {
837 if (other.standardDevs != null) {
838 return false;
839 }
840 } else if (!standardDevs.equals(other.standardDevs)) {
841 return false;
842 }
843 if (stateUuids == null) {
844 if (other.stateUuids != null) {
845 return false;
846 }
847 } else if (!stateUuids.equals(other.stateUuids)) {
848 return false;
849 }
850 if (upperBounds == null) {
851 if (other.upperBounds != null) {
852 return false;
853 }
854 } else if (!upperBounds.equals(other.upperBounds)) {
855 return false;
856 }
857 if (variances == null) {
858 if (other.variances != null) {
859 return false;
860 }
861 } else if (!variances.equals(other.variances)) {
862 return false;
863 }
864 return true;
865 }
866
867 private DescriptiveDataSetService getOuterType() {
868 return DescriptiveDataSetService.this;
869 }
870
871
872
873 }
874
875 }