ref #8754: implement delete method for descriptiveDataSet
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / DescriptiveDataSetService.java
index 981e2d5cf55e21758cead1fb01a2e2c4a259b585..ac28361c724123918ef635e82079ca51c83a7e93 100644 (file)
@@ -1,9 +1,9 @@
 package eu.etaxonomy.cdm.api.service;
 
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -18,31 +18,28 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
+import eu.etaxonomy.cdm.api.service.config.IdentifiableServiceConfiguratorImpl;
 import eu.etaxonomy.cdm.api.service.dto.RowWrapperDTO;
 import eu.etaxonomy.cdm.api.service.dto.SpecimenRowWrapperDTO;
 import eu.etaxonomy.cdm.api.service.dto.TaxonRowWrapperDTO;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
-import eu.etaxonomy.cdm.common.monitor.IRemotingProgressMonitor;
-import eu.etaxonomy.cdm.common.monitor.RemotingProgressMonitorThread;
 import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
 import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.common.Marker;
-import eu.etaxonomy.cdm.model.common.MarkerType;
-import eu.etaxonomy.cdm.model.common.OriginalSourceType;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
-import eu.etaxonomy.cdm.model.description.Character;
 import eu.etaxonomy.cdm.model.description.DescriptionBase;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.DescriptionType;
 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
 import eu.etaxonomy.cdm.model.description.DescriptiveSystemRole;
 import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
+import eu.etaxonomy.cdm.model.description.PolytomousKey;
 import eu.etaxonomy.cdm.model.description.QuantitativeData;
 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
-import eu.etaxonomy.cdm.model.description.StateData;
 import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
-import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.description.TextData;
 import eu.etaxonomy.cdm.model.location.NamedArea;
@@ -54,13 +51,17 @@ import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 import eu.etaxonomy.cdm.persistence.dao.description.IDescriptiveDataSetDao;
-import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
+import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
 import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
+import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
+import eu.etaxonomy.cdm.persistence.dto.TermDto;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.generate.PolytomousKeyGenerator;
+import eu.etaxonomy.cdm.strategy.generate.PolytomousKeyGeneratorConfigurator;
 
 @Service
-@Transactional(readOnly = false)
+@Transactional(readOnly=true)
 public class DescriptiveDataSetService
         extends IdentifiableServiceBase<DescriptiveDataSet, IDescriptiveDataSetDao>
         implements IDescriptiveDataSetService {
@@ -74,16 +75,16 @@ public class DescriptiveDataSetService
     private ITaxonService taxonService;
 
     @Autowired
-    private IDescriptionService descriptionService;
+    private IPolytomousKeyService polytomousKeyService;
 
     @Autowired
-    private ITaxonNodeService taxonNodeService;
+    private IDefinedTermDao termDao;
 
     @Autowired
-    private ITaxonNodeDao taxonNodeDao;
+    private IDescriptionService descriptionService;
 
     @Autowired
-    private IProgressMonitorService progressMonitorService;
+    private ITaxonNodeService taxonNodeService;
 
        @Override
        @Autowired
@@ -108,24 +109,9 @@ public class DescriptiveDataSetService
         return dao.getDescriptiveDataSetUuidAndTitleCache( limitOfInitialElements, pattern);
     }
 
-
-    @Override
-    @Transactional
-    public UUID monitGetRowWrapper(DescriptiveDataSet descriptiveDataSet) {
-        RemotingProgressMonitorThread monitorThread = new RemotingProgressMonitorThread() {
-            @Override
-            public Serializable doRun(IRemotingProgressMonitor monitor) {
-                return getRowWrapper(descriptiveDataSet, monitor);
-            }
-        };
-        UUID uuid = progressMonitorService.registerNewRemotingMonitor(monitorThread);
-        monitorThread.setPriority(3);
-        monitorThread.start();
-        return uuid;
-    }
-
        @Override
-       public ArrayList<RowWrapperDTO> getRowWrapper(DescriptiveDataSet descriptiveDataSet, IProgressMonitor monitor) {
+       public ArrayList<RowWrapperDTO> getRowWrapper(UUID descriptiveDataSetUuid, IProgressMonitor monitor) {
+           DescriptiveDataSet descriptiveDataSet = load(descriptiveDataSetUuid);
            monitor.beginTask("Load row wrapper", descriptiveDataSet.getDescriptions().size());
            ArrayList<RowWrapperDTO> wrappers = new ArrayList<>();
            Set<DescriptionBase> descriptions = descriptiveDataSet.getDescriptions();
@@ -134,11 +120,17 @@ public class DescriptiveDataSetService
                 return new ArrayList<>();
             }
             RowWrapperDTO rowWrapper = null;
-            if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class)){
-                rowWrapper = createTaxonRowWrapper(HibernateProxyHelper.deproxy(description, TaxonDescription.class), descriptiveDataSet);
-            }
-            else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)){
-                rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSet, false);
+            // only viable descriptions are aggregated, literature or default descriptions
+            if(HibernateProxyHelper.isInstanceOf(description, TaxonDescription.class) &&
+                    (description.isAggregatedStructuredDescription()
+                            || description.getTypes().contains(DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION)
+                            || description.getTypes().contains(DescriptionType.SECONDARY_DATA)
+                            )){
+                rowWrapper = createTaxonRowWrapper(description.getUuid(), descriptiveDataSet.getUuid());
+            }
+            else if (HibernateProxyHelper.isInstanceOf(description, SpecimenDescription.class)&&
+                    !description.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE)){
+                rowWrapper = createSpecimenRowWrapper(HibernateProxyHelper.deproxy(description, SpecimenDescription.class), descriptiveDataSetUuid);
             }
             if(rowWrapper!=null){
                 wrappers.add(rowWrapper);
@@ -151,9 +143,19 @@ public class DescriptiveDataSetService
     @Override
     public Collection<SpecimenNodeWrapper> loadSpecimens(DescriptiveDataSet descriptiveDataSet){
         List<UUID> filteredNodes = findFilteredTaxonNodes(descriptiveDataSet);
+        if(filteredNodes.isEmpty()){
+            return Collections.EMPTY_SET;
+        }
         return occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(filteredNodes, null, null);
     }
 
+    @Override
+    public Collection<SpecimenNodeWrapper> loadSpecimens(UUID descriptiveDataSetUuid){
+        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
+        return loadSpecimens(dataSet);
+    }
+
+
     @Override
     public List<UUID> findFilteredTaxonNodes(DescriptiveDataSet descriptiveDataSet){
         TaxonNodeFilter filter = TaxonNodeFilter.NewRankInstance(descriptiveDataSet.getMinRank(), descriptiveDataSet.getMaxRank());
@@ -169,21 +171,51 @@ public class DescriptiveDataSetService
         return taxonNodeService.load(findFilteredTaxonNodes(descriptiveDataSet), propertyPaths);
     }
 
-    private TaxonNode findTaxonNodeForDescription(TaxonNode taxonNode, SpecimenOrObservationBase specimen){
-        Collection<SpecimenNodeWrapper> nodeWrapper = occurrenceService.listUuidAndTitleCacheByAssociatedTaxon(Arrays.asList(taxonNode.getUuid()), null, null);
-        for (SpecimenNodeWrapper specimenNodeWrapper : nodeWrapper) {
-            if(specimenNodeWrapper.getUuidAndTitleCache().getId().equals(specimen.getId())){
-                return taxonNode;
+    @Override
+    public TaxonDescription findDefaultDescription(UUID specimenDescriptionUuid, UUID dataSetUuid){
+        SpecimenDescription specimenDescription = (SpecimenDescription) descriptionService.load(specimenDescriptionUuid);
+        DescriptiveDataSet dataSet = load(dataSetUuid);
+        TaxonNode node = findTaxonNodeForDescription(specimenDescription, dataSet);
+        return recurseDefaultDescription(node, dataSet);
+    }
+
+    private TaxonDescription recurseDefaultDescription(TaxonNode node, DescriptiveDataSet dataSet){
+        TaxonDescription defaultDescription = null;
+        if(node!=null && node.getTaxon()!=null){
+            defaultDescription = findTaxonDescriptionByDescriptionType(dataSet, node.getTaxon(), DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION);
+            if(defaultDescription==null && node.getParent()!=null){
+                defaultDescription = recurseDefaultDescription(node.getParent(), dataSet);
+            }
+        }
+        return defaultDescription;
+    }
+
+    private TaxonNode findTaxonNodeForDescription(SpecimenDescription description, DescriptiveDataSet descriptiveDataSet){
+        SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
+        //get taxon node
+
+        Set<IndividualsAssociation> associations = (Set<IndividualsAssociation>)descriptiveDataSet.getDescriptions()
+                .stream()
+                .flatMap(desc->desc.getElements().stream())// put all description element in one stream
+                .filter(element->element instanceof IndividualsAssociation)
+                .map(ia->(IndividualsAssociation)ia)
+                .collect(Collectors.toSet());
+        Classification classification = descriptiveDataSet.getTaxonSubtreeFilter().iterator().next().getClassification();
+        for (IndividualsAssociation individualsAssociation : associations) {
+            if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(specimen)){
+                return ((TaxonDescription) individualsAssociation.getInDescription()).getTaxon().getTaxonNode(classification);
             }
         }
         return null;
     }
 
     @Override
-    public TaxonRowWrapperDTO createTaxonRowWrapper(TaxonDescription description,
-            DescriptiveDataSet descriptiveDataSet) {
+    public TaxonRowWrapperDTO createTaxonRowWrapper(UUID taxonDescriptionUuid, UUID descriptiveDataSetUuid) {
         TaxonNode taxonNode = null;
         Classification classification = null;
+        TaxonDescription description = (TaxonDescription) descriptionService.load(taxonDescriptionUuid,
+                Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature"));
+        DescriptiveDataSet descriptiveDataSet = dao.load(descriptiveDataSetUuid, null);
         Optional<TaxonNode> first = descriptiveDataSet.getTaxonSubtreeFilter().stream()
                 .filter(node->node.getClassification()!=null).findFirst();
         Optional<Classification> classificationOptional = first.map(node->node.getClassification());
@@ -192,38 +224,78 @@ public class DescriptiveDataSetService
             Taxon taxon = (Taxon) taxonService.load(description.getTaxon().getId(), Arrays.asList("taxonNodes", "taxonNodes.classification"));
             taxonNode = taxon.getTaxonNode(classification);
         }
-        return new TaxonRowWrapperDTO(description, taxonNode);
+        return new TaxonRowWrapperDTO(description, new TaxonNodeDto(taxonNode));
     }
 
     @Override
-    public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, DescriptiveDataSet descriptiveDataSet,
-            boolean createDefaultTaxonDescription){
-           SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
-           TaxonNode taxonNode = null;
+    @Transactional(readOnly=false)
+    public UpdateResult addRowWrapperToDataset(Collection<SpecimenNodeWrapper> wrappers, UUID datasetUuid){
+        UpdateResult result = new UpdateResult();
+        DescriptiveDataSet dataSet = load(datasetUuid);
+        result.setCdmEntity(dataSet);
+
+        List<UUID> taxonUuids = wrappers.stream().map(wrapper->wrapper.getTaxonNode().getTaxon().getUuid()).collect(Collectors.toList());
+        List<TaxonBase> taxa = taxonService.load(taxonUuids, Arrays.asList(new String[]{"descriptions"}));
+
+        for (SpecimenNodeWrapper wrapper : wrappers) {
+            Optional<TaxonBase> findAny = taxa.stream().filter(taxon->taxon.getUuid().equals(wrapper.getTaxonNode().getTaxon().getUuid())).findAny();
+            if(!findAny.isPresent()){
+                result.addException(new IllegalArgumentException("Could not create wrapper for "+wrapper.getUuidAndTitleCache().getTitleCache()));
+                continue;
+            }
+            Taxon taxon = (Taxon) findAny.get();
+            UUID taxonDescriptionUuid = wrapper.getTaxonDescriptionUuid();
+            TaxonDescription taxonDescription = null;
+            if(taxonDescriptionUuid!=null){
+                taxonDescription = (TaxonDescription) descriptionService.load(taxonDescriptionUuid);
+            }
+            if(taxonDescription==null){
+                Optional<TaxonDescription> associationDescriptionOptional = taxon.getDescriptions().stream()
+                        .filter(desc->desc.getTypes().contains(DescriptionType.INDIVIDUALS_ASSOCIATION))
+                        .findFirst();
+                if(!associationDescriptionOptional.isPresent()){
+                    taxonDescription = TaxonDescription.NewInstance(taxon);
+                }
+                else{
+                    taxonDescription = associationDescriptionOptional.get();
+                }
+
+                SpecimenOrObservationBase specimen = occurrenceService.load(wrapper.getUuidAndTitleCache().getUuid());
+                IndividualsAssociation association = IndividualsAssociation.NewInstance(specimen);
+                taxonDescription.addElement(association);
+                taxonService.saveOrUpdate(taxon);
+                result.addUpdatedObject(taxon);
+            }
+            SpecimenDescription specimenDescription = findSpecimenDescription(datasetUuid, wrapper.getUuidAndTitleCache().getUuid(), true);
+            SpecimenRowWrapperDTO rowWrapper = createSpecimenRowWrapper(specimenDescription, wrapper.getTaxonNode().getUuid(), datasetUuid);
+            if(rowWrapper==null){
+                result.addException(new IllegalArgumentException("Could not create wrapper for "+specimenDescription));
+                continue;
+            }
+            //add specimen description to data set
+            rowWrapper.getDescription().addDescriptiveDataSet(dataSet);
+            //add taxon description with IndividualsAssociation to the specimen to data set
+            taxonDescription.addDescriptiveDataSet(dataSet);
+
+            result.addUpdatedObject(rowWrapper.getDescription());
+            result.addUpdatedObject(taxonDescription);
+        }
+        saveOrUpdate(dataSet);
+        return result;
+    }
+
+    private SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID taxonNodeUuid,
+            UUID datasetUuid) {
+        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
+        DescriptiveDataSet descriptiveDataSet = load(datasetUuid);
+        SpecimenOrObservationBase specimen = description.getDescribedSpecimenOrObservation();
+        //supplemental information
+        if(taxonNode==null){
+            taxonNode = findTaxonNodeForDescription(description, descriptiveDataSet);
+        }
         FieldUnit fieldUnit = null;
         String identifier = null;
         NamedArea country = null;
-        //supplemental information
-        //get taxon node
-        Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
-        for (TaxonNode node : taxonSubtreeFilter) {
-            //check for node
-            node = taxonNodeService.load(node.getId(), Arrays.asList("taxon"));
-            taxonNode = findTaxonNodeForDescription(node, specimen);
-            if(taxonNode!=null){
-                break;
-            }
-            else{
-                //check for child nodes
-                List<TaxonNode> allChildren = taxonNodeService.loadChildNodesOfTaxonNode(node, Arrays.asList("taxon"), true, true, null);
-                for (TaxonNode child : allChildren) {
-                    taxonNode = findTaxonNodeForDescription(child, specimen);
-                    if(taxonNode!=null){
-                        break;
-                    }
-                }
-            }
-        }
         if(taxonNode==null){
             return null;
         }
@@ -251,183 +323,144 @@ public class DescriptiveDataSetService
             country = fieldUnit.getGatheringEvent().getCountry();
         }
         //get default taxon description
-        TaxonDescription defaultTaxonDescription = findDefaultTaxonDescription(descriptiveDataSet.getUuid(),
-                taxonNode.getUuid(), createDefaultTaxonDescription);
+        TaxonDescription defaultTaxonDescription = findDefaultDescription(description.getUuid(), descriptiveDataSet.getUuid());
         TaxonRowWrapperDTO taxonRowWrapper = defaultTaxonDescription != null
-                ? createTaxonRowWrapper(defaultTaxonDescription, descriptiveDataSet) : null;
-        return new SpecimenRowWrapperDTO(description, taxonNode, fieldUnit, identifier, country, taxonRowWrapper);
+                ? createTaxonRowWrapper(defaultTaxonDescription.getUuid(), descriptiveDataSet.getUuid()) : null;
+        SpecimenRowWrapperDTO specimenRowWrapperDTO = new SpecimenRowWrapperDTO(description, new TaxonNodeDto(taxonNode), fieldUnit, identifier, country);
+        specimenRowWrapperDTO.setDefaultDescription(taxonRowWrapper);
+        return specimenRowWrapperDTO;
+    }
+
+    @Override
+    public SpecimenRowWrapperDTO createSpecimenRowWrapper(SpecimenDescription description, UUID descriptiveDataSetUuid){
+        return createSpecimenRowWrapper(description, null, descriptiveDataSetUuid);
        }
 
     @Override
     @Transactional(readOnly = false)
-    public void updateTitleCache(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
+    public UpdateResult updateCaches(Class<? extends DescriptiveDataSet> clazz, Integer stepSize,
             IIdentifiableEntityCacheStrategy<DescriptiveDataSet> cacheStrategy, IProgressMonitor monitor) {
         if (clazz == null) {
             clazz = DescriptiveDataSet.class;
         }
-        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
     }
 
-    @Override
-    public TaxonDescription findDefaultTaxonDescription(UUID descriptiveDataSetUuid, UUID taxonNodeUuid, boolean create){
-        DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
-        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon", "taxon.descriptions", "taxon.descriptions.markers"));
-        Set<DescriptionBase> dataSetDescriptions = dataSet.getDescriptions();
-        //filter out COMPUTED descriptions
-        List<TaxonDescription> nonComputedDescriptions = taxonNode.getTaxon().getDescriptions().stream()
-                .filter(desc -> desc.getMarkers().stream()
-                        .noneMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
-                .collect(Collectors.toList());
-        for (TaxonDescription taxonDescription : nonComputedDescriptions) {
-            for (DescriptionBase description : dataSetDescriptions) {
-                if(description.getUuid().equals(taxonDescription.getUuid())){
-                    return HibernateProxyHelper.deproxy(descriptionService.load(taxonDescription.getUuid(),
-                            Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
-                }
-            }
+    private TaxonDescription findTaxonDescriptionByDescriptionType(DescriptiveDataSet dataSet, Taxon taxon, DescriptionType descriptionType){
+        Optional<TaxonDescription> first = taxon.getDescriptions().stream()
+                .filter(desc -> desc.getTypes().stream().anyMatch(type -> type.equals(descriptionType)))
+                .filter(desc -> dataSet.getDescriptions().contains(desc))
+                .findFirst();
+        if(first.isPresent()){
+            return HibernateProxyHelper.deproxy(descriptionService.load(first.get().getUuid(),
+                  Arrays.asList("taxon", "descriptionElements", "descriptionElements.feature")), TaxonDescription.class);
         }
-        if(!create){
-            return null;
-        }
-        TaxonRowWrapperDTO taxonRowWrapperDTO = createTaxonDescription(descriptiveDataSetUuid, taxonNodeUuid, MarkerType.USE(), true);
-        TaxonDescription newTaxonDescription = taxonRowWrapperDTO.getDescription();
-        return newTaxonDescription;
+        return null;
     }
 
     @Override
-    @Transactional(readOnly=false)
-    public UpdateResult aggregateTaxonDescription(UUID taxonNodeUuid, UUID descriptiveDataSetUuid,
-            IRemotingProgressMonitor monitor){
-        UpdateResult result = new UpdateResult();
-
-        TaxonNode node = taxonNodeService.load(taxonNodeUuid);
-        Taxon taxon = HibernateProxyHelper.deproxy(taxonService.load(node.getTaxon().getUuid()), Taxon.class);
-        result.setCdmEntity(taxon);
-
-        //get all "computed" descriptions from all sub nodes
-        List<TaxonNode> childNodes = taxonNodeDao.listChildrenOf(node, null, null, true, false, null);
-        List<TaxonDescription> computedDescriptions = new ArrayList<>();
-
-        childNodes.stream().map(childNode -> childNode.getTaxon())
-                .forEach(childTaxon -> childTaxon.getDescriptions().stream()
-                        // filter out non-computed descriptions
-                        .filter(description -> description.getMarkers().stream()
-                                .anyMatch(marker -> marker.getMarkerType().equals(MarkerType.COMPUTED())))
-                        // add them to the list
-                        .forEach(computedDescription -> computedDescriptions.add(computedDescription)));
-
-        UpdateResult aggregateDescription = aggregateDescription(taxon, computedDescriptions,
-                "[Taxon Descriptions]"+taxon.getTitleCache(), descriptiveDataSetUuid);
-        result.includeResult(aggregateDescription);
-        result.setCdmEntity(aggregateDescription.getCdmEntity());
-        aggregateDescription.setCdmEntity(null);
-        return result;
+    public TaxonDescription findTaxonDescriptionByDescriptionType(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
+        DescriptiveDataSet dataSet = load(dataSetUuid);
+        TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid);
+        return findTaxonDescriptionByDescriptionType(dataSet, taxonNode.getTaxon(), descriptionType);
     }
 
     @Override
     @Transactional(readOnly=false)
-    public UpdateResult aggregateDescription(UUID taxonUuid, List<UUID> descriptionUuids, String descriptionTitle
-            , UUID descriptiveDataSetUuid) {
+    public UpdateResult generatePolytomousKey(UUID descriptiveDataSetUuid, UUID taxonUuid) {
         UpdateResult result = new UpdateResult();
 
-        TaxonBase taxonBase = taxonService.load(taxonUuid);
-        if(!(taxonBase instanceof Taxon)){
-            result.addException(new ClassCastException("The given taxonUUID does not belong to a taxon"));
-            result.setError();
-            return result;
+        PolytomousKeyGeneratorConfigurator keyConfig = new PolytomousKeyGeneratorConfigurator();
+        DescriptiveDataSet descriptiveDataSet = load(descriptiveDataSetUuid);
+        keyConfig.setDataSet(descriptiveDataSet);
+        PolytomousKey key = new PolytomousKeyGenerator().invoke(keyConfig);
+        IdentifiableServiceConfiguratorImpl<PolytomousKey> serviceConfig= new IdentifiableServiceConfiguratorImpl<>();
+        serviceConfig.setTitleSearchString(descriptiveDataSet.getTitleCache());
+        List<PolytomousKey> list = polytomousKeyService.findByTitle(serviceConfig).getRecords();
+        if(list!=null){
+            list.forEach(polytomousKey->polytomousKeyService.delete(polytomousKey));
         }
-        Taxon taxon = (Taxon)taxonBase;
+        key.setTitleCache(descriptiveDataSet.getTitleCache(), true);
 
-        List<DescriptionBase> descriptions = descriptionService.load(descriptionUuids, null);
+        Taxon taxon = (Taxon) taxonService.load(taxonUuid);
+        key.addTaxonomicScope(taxon);
 
-        UpdateResult aggregateDescriptionResult = aggregateDescription(taxon, descriptions, descriptionTitle, descriptiveDataSetUuid);
-        result.setCdmEntity(aggregateDescriptionResult.getCdmEntity());
-        aggregateDescriptionResult.setCdmEntity(null);
-        result.includeResult(aggregateDescriptionResult);
+        polytomousKeyService.saveOrUpdate(key);
+
+        result.setCdmEntity(key);
+        result.addUpdatedObject(taxon);
         return result;
     }
 
-    @SuppressWarnings("unchecked")
-    private UpdateResult aggregateDescription(Taxon taxon, List<? extends DescriptionBase> descriptions, String descriptionTitle
-            , UUID descriptiveDataSetUuid) {
-        UpdateResult result = new UpdateResult();
-        Map<Character, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
-
+    @Override
+    @Transactional(readOnly=false)
+    public DeleteResult removeDescription(UUID descriptionUuid, UUID descriptiveDataSetUuid) {
+        DeleteResult result = new DeleteResult();
         DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
-        if(dataSet==null){
-            result.addException(new IllegalArgumentException("Could not find data set for uuid "+descriptiveDataSetUuid));
-            result.setAbort();
-            return result;
+        DescriptionBase descriptionBase = descriptionService.load(descriptionUuid);
+        if(dataSet==null || descriptionBase==null){
+            result.setError();
         }
-
-        //extract all character description elements
-        descriptions.forEach(description->{
-            description.getElements()
-            .stream()
-            //filter out elements that do not have a Characters as Feature
-            .filter(element->HibernateProxyHelper.isInstanceOf(((DescriptionElementBase)element).getFeature(), Character.class))
-            .forEach(ele->{
-                DescriptionElementBase descriptionElement = (DescriptionElementBase)ele;
-                List<DescriptionElementBase> list = featureToElementMap.get(descriptionElement.getFeature());
-                if(list==null){
-                    list = new ArrayList<>();
+        else{
+            boolean success = dataSet.removeDescription(descriptionBase);
+            result.addDeletedObject(descriptionBase);
+            // remove taxon description with IndividualsAssociation from data set
+            if(descriptionBase instanceof SpecimenDescription){
+                @SuppressWarnings("cast")
+                Set<IndividualsAssociation> associations = (Set<IndividualsAssociation>)dataSet.getDescriptions()
+                        .stream()
+                        .flatMap(desc->desc.getElements().stream())// put all description element in one stream
+                        .filter(element->element instanceof IndividualsAssociation)
+                        .map(ia->(IndividualsAssociation)ia)
+                        .collect(Collectors.toSet());
+                Classification classification = dataSet.getTaxonSubtreeFilter().iterator().next().getClassification();
+                for (IndividualsAssociation individualsAssociation : associations) {
+                    if(individualsAssociation.getAssociatedSpecimenOrObservation().equals(descriptionBase.getDescribedSpecimenOrObservation())){
+                        dataSet.removeDescription(individualsAssociation.getInDescription());
+                        result.addDeletedObject(individualsAssociation.getInDescription());
+                    }
                 }
-                list.add(descriptionElement);
-                featureToElementMap.put(HibernateProxyHelper.deproxy(descriptionElement.getFeature(), Character.class), list);
-            });
-        });
+            }
+            result.addUpdatedObject(dataSet);
+            result.setStatus(success?Status.OK:Status.ERROR);
+        }
+        return result;
+    }
 
-        TaxonDescription description = TaxonDescription.NewInstance(taxon);
-        description.setTitleCache("[Aggregation] "+descriptionTitle, true);
-        description.addMarker(Marker.NewInstance(MarkerType.COMPUTED(), true));
-        IdentifiableSource source = IdentifiableSource.NewInstance(OriginalSourceType.Aggregation);
-        description.addSource(source);
-        description.addDescriptiveDataSet(dataSet);
-
-        featureToElementMap.forEach((feature, elements)->{
-            //aggregate categorical data
-            if(feature.isSupportsCategoricalData()){
-                CategoricalData aggregate = CategoricalData.NewInstance(feature);
-                elements.stream()
-                .filter(element->element instanceof CategoricalData)
-                .forEach(categoricalData->((CategoricalData)categoricalData).getStateData()
-                        .forEach(stateData->aggregate.addStateData((StateData) stateData.clone())));
-                description.addElement(aggregate);
-            }
-            //aggregate quantitative data
-            else if(feature.isSupportsQuantitativeData()){
-                QuantitativeData aggregate = QuantitativeData.NewInstance(feature);
-                elements.stream()
-                .filter(element->element instanceof QuantitativeData)
-                .forEach(categoricalData->((QuantitativeData)categoricalData).getStatisticalValues()
-                        .forEach(statisticalValue->aggregate.addStatisticalValue((StatisticalMeasurementValue) statisticalValue.clone())));
-                description.addElement(aggregate);
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult delete(UUID datasetUuid){
+        DescriptiveDataSet dataSet = dao.load(datasetUuid);
+        DeleteResult result = new DeleteResult();
+        if (!dataSet.getDescriptions().isEmpty()){
+            Set<DescriptionBase> descriptions = new HashSet();;
+            for (DescriptionBase desc: dataSet.getDescriptions()){
+                descriptions.add(desc);
             }
-        });
-        result.addUpdatedObject(taxon);
-        result.setCdmEntity(description);
+            DeleteResult descriptionResult;
+            for (DescriptionBase desc: descriptions){
+                dataSet.removeDescription(desc);
+                descriptionResult = descriptionService.deleteDescription(desc);
+                result.includeResult(descriptionResult);
+            }
+
+
+        }
+        dao.delete(dataSet);
+        result.addDeletedObject(dataSet);
         return result;
     }
 
     @Override
-    public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, MarkerType markerType, boolean markerFlag){
+    @Transactional(readOnly=false)
+    public TaxonRowWrapperDTO createTaxonDescription(UUID dataSetUuid, UUID taxonNodeUuid, DescriptionType descriptionType){
         DescriptiveDataSet dataSet = load(dataSetUuid);
         TaxonNode taxonNode = taxonNodeService.load(taxonNodeUuid, Arrays.asList("taxon"));
         TaxonDescription newTaxonDescription = TaxonDescription.NewInstance(taxonNode.getTaxon());
-        String tag = "";
-        if(markerFlag){
-            if(markerType.equals(MarkerType.USE())){
-                tag = "[Default]";
-            }
-            else if(markerType.equals(MarkerType.IN_BIBLIOGRAPHY())){
-                tag = "[Literature]";
-            }
-        }
-        newTaxonDescription.setTitleCache(tag+" "+dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
-        if(markerType!=null){
-            newTaxonDescription.addMarker(Marker.NewInstance(markerType, markerFlag));
-        }
-        dataSet.getDescriptiveSystem().getDistinctFeatures().forEach(wsFeature->{
+        newTaxonDescription.setTitleCache(dataSet.getLabel()+": "+newTaxonDescription.generateTitle(), true); //$NON-NLS-2$
+        newTaxonDescription.getTypes().add(descriptionType);
+
+        dataSet.getDescriptiveSystem().getDistinctTerms().forEach(wsFeature->{
             if(wsFeature.isSupportsCategoricalData()){
                 newTaxonDescription.addElement(CategoricalData.NewInstance(wsFeature));
             }
@@ -437,15 +470,21 @@ public class DescriptiveDataSetService
         });
         dataSet.addDescription(newTaxonDescription);
 
-        return createTaxonRowWrapper(newTaxonDescription, dataSet);
+        return createTaxonRowWrapper(newTaxonDescription.getUuid(), dataSet.getUuid());
     }
 
     @Override
-    public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, UUID specimenUuid){
+    public List<TermDto> getSupportedStatesForFeature(UUID featureUuid){
+        return termDao.getSupportedStatesForFeature(featureUuid);
+    }
+
+    @Override
+    @Transactional(readOnly=false)
+    public SpecimenDescription findSpecimenDescription(UUID descriptiveDataSetUuid, UUID specimenUuid, boolean addDatasetSource){
         DescriptiveDataSet dataSet = load(descriptiveDataSetUuid);
         SpecimenOrObservationBase specimen = occurrenceService.load(specimenUuid);
 
-        Set<Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctFeatures();
+        Set<? extends Feature> datasetFeatures = dataSet.getDescriptiveSystem().getDistinctTerms();
         List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
 
         for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
@@ -461,7 +500,7 @@ public class DescriptiveDataSetService
             for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
                 Feature feature = specimenDescriptionElement.getFeature();
                 specimenDescriptionFeatures.add(feature);
-                if(datasetFeatures.contains(feature)){
+                if(datasetFeatures.contains(feature) && RowWrapperDTO.hasData(specimenDescriptionElement)){
                     matchingDescriptionElements.add(specimenDescriptionElement);
                 }
             }
@@ -523,27 +562,19 @@ public class DescriptiveDataSetService
                     }
                 });
             } catch (CloneNotSupportedException e) {
-//                MessagingUtils.error(CharacterMatrix.class, e);
+                //nothing
             }
         }
 
-        //add all remaining description elements to the new description
-        for(Feature wsFeature:datasetFeatures){
-            boolean featureFound = false;
-            for(DescriptionElementBase element:newDesription.getElements()){
-                if(element.getFeature().equals(wsFeature)){
-                    featureFound = true;
-                    break;
+        //add sources of data set
+        if(addDatasetSource){
+            dataSet.getSources().forEach(source->{
+                try {
+                    newDesription.addSource((IdentifiableSource) source.clone());
+                } catch (CloneNotSupportedException e) {
+                    //nothing
                 }
-            }
-            if(!featureFound){
-                if(wsFeature.isSupportsCategoricalData()){
-                    newDesription.addElement(CategoricalData.NewInstance(wsFeature));
-                }
-                else if(wsFeature.isSupportsQuantitativeData()){
-                    newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
-                }
-            }
+            });
         }
         return newDesription;
 
@@ -726,6 +757,8 @@ public class DescriptiveDataSetService
             return DescriptiveDataSetService.this;
         }
 
+
+
     }
 
 }