ref #2380: improve implementation of Dto for use in taxonnavigator
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / ClassificationServiceImpl.java
index 590a32f2cf6eca0b12626cdb566c6039da0a2226..9de05cfb176b61c0770fefc6d907e0ff2d8d9a63 100644 (file)
@@ -1,4 +1,3 @@
-// $Id$
 /**
 * Copyright (C) 2007 EDIT
 * European Distributed Institute of Taxonomy
 package eu.etaxonomy.cdm.api.service;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
 
+import javax.persistence.EntityNotFoundException;
+
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import eu.etaxonomy.cdm.api.service.config.CreateHierarchyForClassificationConfigurator;
+import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
+import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
+import eu.etaxonomy.cdm.api.service.dto.EntityDTO;
+import eu.etaxonomy.cdm.api.service.dto.GroupedTaxonDTO;
+import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
+import eu.etaxonomy.cdm.api.service.dto.TaxonInContextDTO;
 import eu.etaxonomy.cdm.api.service.pager.Pager;
 import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+import eu.etaxonomy.cdm.hibernate.HHH_9751_Util;
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;
+import eu.etaxonomy.cdm.model.common.ITreeNode;
+import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.common.TreeIndex;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.media.Media;
 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
 import eu.etaxonomy.cdm.model.media.MediaUtils;
-import eu.etaxonomy.cdm.model.name.NonViralName;
+import eu.etaxonomy.cdm.model.name.INonViralName;
 import eu.etaxonomy.cdm.model.name.Rank;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.name.TaxonName;
+import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.taxon.Classification;
 import eu.etaxonomy.cdm.model.taxon.ITaxonNodeComparator;
 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
+import eu.etaxonomy.cdm.model.taxon.Synonym;
 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.model.taxon.TaxonRelationshipType;
+import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
 import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
+import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
+import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
+import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.persistence.query.OrderHint;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
@@ -71,6 +96,12 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     @Autowired
     private ITaxonDao taxonDao;
 
+    @Autowired
+    private ITaxonNodeService taxonNodeService;
+
+    @Autowired
+    private IDefinedTermDao termDao;
+
     @Autowired
     private IBeanInitializer defaultBeanInitializer;
 
@@ -105,6 +136,53 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return taxonNodeDao.load(taxonNodeUuid, propertyPaths);
     }
 
+    @Override
+    @Transactional(readOnly = false)
+    public UpdateResult cloneClassification(UUID classificationUuid,
+               String name, Reference sec, TaxonRelationshipType relationshipType) {
+        UpdateResult result = new UpdateResult();
+       Classification classification = load(classificationUuid);
+       Classification clone = Classification.NewInstance(name);
+       clone.setReference(sec);
+
+       //clone taxa and taxon nodes
+       List<TaxonNode> childNodes = classification.getRootNode().getChildNodes();
+       for (TaxonNode taxonNode : childNodes) {
+               addChildTaxa(taxonNode, null, clone, relationshipType);
+       }
+       dao.saveOrUpdate(clone);
+       result.setCdmEntity(clone);
+       return result;
+    }
+
+    private void addChildTaxa(TaxonNode originalParentNode, TaxonNode cloneParentNode, Classification classification, TaxonRelationshipType relationshipType){
+        Reference reference = classification.getReference();
+       Taxon cloneTaxon = (Taxon) HibernateProxyHelper.deproxy(originalParentNode.getTaxon(), Taxon.class).clone();
+       cloneTaxon.setSec(reference);
+               String microReference = null;
+               List<TaxonNode> originalChildNodes = originalParentNode.getChildNodes();
+               HHH_9751_Util.removeAllNull(originalChildNodes);
+
+               //add relation between taxa
+               if (relationshipType != null){
+                   cloneTaxon.addTaxonRelation(originalParentNode.getTaxon(), relationshipType, reference, microReference);
+               }
+
+               TaxonNode cloneChildNode = null;
+       //add taxon node to either parent node or classification (no parent node)
+       if(cloneParentNode==null){
+               cloneChildNode = classification.addChildTaxon(cloneTaxon, reference, microReference);
+       }
+       else{
+               cloneChildNode = cloneParentNode.addChildTaxon(cloneTaxon, reference, microReference);
+       }
+       taxonNodeDao.saveOrUpdate(cloneChildNode);
+       //add children
+               for (TaxonNode originalChildNode : originalChildNodes) {
+               addChildTaxa(originalChildNode, cloneChildNode, classification, relationshipType);
+       }
+    }
+
     @Override
     public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
             Integer pageIndex, List<String> propertyPaths) {
@@ -146,8 +224,9 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                 }
             }
         }
-
-        Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
+//        long start_t = System.currentTimeMillis();
+        Collections.sort(results, taxonNodeComparator); // TODO is ordering during the hibernate query in the dao possible?
+//        System.err.println("service.pageRankSpecificRootNodes() - Collections.sort(results,  taxonNodeComparator) " + (System.currentTimeMillis() - start_t));
         return new DefaultPagerImpl<TaxonNode>(pageIndex, (int) totalNumberOfResults, pageSize, results);
 
     }
@@ -162,6 +241,9 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
 
         TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
+        if(baseRank != null){
+            baseRank = (Rank) termDao.load(baseRank.getUuid());
+        }
         List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
         pathToRoot.add(thisNode);
 
@@ -233,6 +315,34 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return results;
     }
 
+    @Override
+    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
+            Integer pageIndex, List<String> propertyPaths){
+
+        Classification classification = dao.load(classificationUuid);
+        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
+
+        long numberOfResults = dao.countSiblingsOf(taxon, classification);
+
+        List<TaxonNode> results;
+        if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
+            results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
+            Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
+        } else {
+            results = new ArrayList<>();
+        }
+
+        return new DefaultPagerImpl<TaxonNode>(pageIndex, numberOfResults, pageSize, results);
+    }
+
+    @Override
+    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
+            Integer pageIndex, List<String> propertyPaths){
+
+        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
+        return pager.getRecords();
+    }
+
     @Override
     public TaxonNode getTaxonNodeByUuid(UUID uuid) {
         return taxonNodeDao.findByUuid(uuid);
@@ -248,6 +358,11 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return treeNode;
     }
 
+    @Override
+    public TaxonNode getRootNode(UUID classificationUuid){
+        return dao.getRootNode(classificationUuid);
+    }
+
     @Override
     public List<Classification> listClassifications(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
         return dao.list(limit, start, orderHints, propertyPaths);
@@ -277,6 +392,14 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return taxonNodeDao.saveAll(taxonNodeCollection);
     }
 
+    @Override
+    public UUID saveClassification(Classification classification) {
+
+       taxonNodeDao.saveOrUpdateAll(classification.getAllNodes());
+       UUID result =dao.saveOrUpdate(classification);
+       return result;
+    }
+
     @Override
     public UUID saveTreeNode(ITaxonTreeNode treeNode) {
         if(treeNode instanceof Classification){
@@ -293,13 +416,28 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     }
 
     @Override
-    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification) {
-        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification);
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern) {
+        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern);
     }
 
     @Override
-    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache() {
-        return dao.getUuidAndTitleCache();
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification,  Integer limit, String pattern) {
+        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,  limit, pattern);
+    }
+
+    @Override
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid ) {
+        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid), null, null);
+    }
+
+    @Override
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification ) {
+        return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, null, null);
+    }
+
+    @Override
+    public List<UuidAndTitleCache<Classification>> getUuidAndTitleCache(Integer limit, String pattern) {
+        return dao.getUuidAndTitleCache(limit, pattern);
     }
 
     @Override
@@ -344,12 +482,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
 
     }
 
-    @Override
-    public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(Taxon taxon, Classification taxTree, List<String> propertyPaths, int size, int height, int widthOrDuration, String[] mimeTypes){
-        TaxonNode node = taxTree.getNode(taxon);
-
-        return getAllMediaForChildNodes(node, propertyPaths, size, height, widthOrDuration, mimeTypes);
-    }
 
     @Override
     @Transactional(readOnly = false)
@@ -371,11 +503,11 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
        if(allNodesOfClassification == null || allNodesOfClassification.isEmpty()){
                return null;
        }
-       Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<String, List<TaxonNode>>();
+       Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
        for(TaxonNode node:allNodesOfClassification){
                final TaxonNode tn = node;
                Taxon taxon = node.getTaxon();
-               NonViralName name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
+               INonViralName name = taxon.getName();
                String genusOrUninomial = name.getGenusOrUninomial();
                //if rank unknown split string and take first word
                if(genusOrUninomial == null){
@@ -413,7 +545,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
      * @param configurator to change certain settings, if null then standard settings will be taken
      * @return new classification with parentNodes for each entry in the map
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @SuppressWarnings({ "unchecked" })
        @Transactional(readOnly = false)
        @Override
     public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
@@ -422,7 +554,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
        Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
 
        final String APPENDIX = "repaired";
-       String titleCache = org.apache.commons.lang.StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
+       String titleCache = StringUtils.isBlank(classification.getTitleCache()) ? " " : classification.getTitleCache() ;
        //TODO classification clone???
        Classification newClassification = Classification.NewInstance(titleCache +" "+ APPENDIX);
        newClassification.setReference(classification.getReference());
@@ -435,9 +567,8 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                for(TaxonNode tNode:listOfTaxonNodes){
                        //take that taxonNode as parent and remove from list with all it possible children
                        //FIXME NPE for name
-                       TaxonNameBase name = tNode.getTaxon().getName();
-                               NonViralName nonViralName = CdmBase.deproxy(name, NonViralName.class);
-                       if(nonViralName.getNameCache().equalsIgnoreCase(genus)){
+                       TaxonName name = tNode.getTaxon().getName();
+                       if(name.getNameCache().equalsIgnoreCase(genus)){
                                TaxonNode clone = (TaxonNode) tNode.clone();
                                if(!tNode.hasChildNodes()){
                                        //FIXME remove classification
@@ -466,10 +597,9 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                if(parentNode == null){
                        //if no match found in list, create parentNode
                        NonViralNameParserImpl parser = NonViralNameParserImpl.NewInstance();
-                       NonViralName nonViralName = parser.parseFullName(genus);
-                       TaxonNameBase taxonNameBase = nonViralName;
+                       TaxonName TaxonName = (TaxonName)parser.parseFullName(genus);
                        //TODO Sec via configurator
-                       Taxon taxon = Taxon.NewInstance(taxonNameBase, null);
+                       Taxon taxon = Taxon.NewInstance(TaxonName, null);
                        parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
                        result.addUpdatedObject(parentNode);
                }
@@ -533,4 +663,326 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                return childNodes;
        }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ClassificationLookupDTO classificationLookup(Classification classification) {
+        return dao.classificationLookup(classification);
+    }
+
+
+    @Override
+    @Transactional
+    public DeleteResult delete(UUID classificationUuid, TaxonDeletionConfigurator config){
+        DeleteResult result = new DeleteResult();
+        Classification classification = dao.findByUuid(classificationUuid);
+        if (classification == null){
+            result.addException(new IllegalArgumentException("The classification does not exist in database."));
+            result.setAbort();
+            return result;
+        }
+        if (!classification.hasChildNodes()){
+            dao.delete(classification);
+            result.addDeletedObject(classification);
+            return result;
+        }
+        if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
+            TaxonNode root = classification.getRootNode();
+            result.includeResult(taxonNodeService.deleteTaxonNode(root, config));
+            dao.delete(classification);
+            result.addDeletedObject(classification);
+
+        }
+
+
+        return result;
+    }
+
+    @Override
+    public List<GroupedTaxonDTO> groupTaxaByHigherTaxon(List<UUID> originalTaxonUuids, UUID classificationUuid, Rank minRank, Rank maxRank){
+        List<GroupedTaxonDTO> result = new ArrayList<>();
+
+        //get treeindex for each taxonUUID
+        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
+
+        //build treeindex list (or tree)
+        //TODO make it work with TreeIndex or move there
+        List<String> treeIndexClosureStr = new ArrayList<>();
+        for (TreeIndex treeIndex : taxonIdTreeIndexMap.values()){
+            String[] splits = treeIndex.toString().substring(1).split(ITreeNode.separator);
+            String currentIndex = ITreeNode.separator;
+            for (String split : splits){
+                if (split.equals("")){
+                    continue;
+                }
+                currentIndex += split + ITreeNode.separator;
+                if (!treeIndexClosureStr.contains(currentIndex) && !split.startsWith(ITreeNode.treePrefix)){
+                    treeIndexClosureStr.add(currentIndex);
+                }
+            }
+        }
+
+        //get rank sortindex for all parent taxa with sortindex <= minRank and sortIndex >= maxRank (if available)
+        Integer minRankOrderIndex = minRank == null ? null : minRank.getOrderIndex();
+        Integer maxRankOrderIndex = maxRank == null ? null : maxRank.getOrderIndex();
+        List<TreeIndex> treeIndexClosure = TreeIndex.NewListInstance(treeIndexClosureStr);
+
+        Map<TreeIndex, Integer> treeIndexSortIndexMapTmp = taxonNodeDao.rankOrderIndexForTreeIndex(treeIndexClosure, minRankOrderIndex, maxRankOrderIndex);
+
+        //remove all treeindex with "exists child in above map(and child.sortindex > xxx)
+        List<TreeIndex> treeIndexList = TreeIndex.sort(treeIndexSortIndexMapTmp.keySet());
+
+        Map<TreeIndex, Integer> treeIndexSortIndexMap = new HashMap<>();
+        TreeIndex lastTreeIndex = null;
+        for (TreeIndex treeIndex : treeIndexList){
+            if (lastTreeIndex != null && lastTreeIndex.hasChild(treeIndex)){
+                treeIndexSortIndexMap.remove(lastTreeIndex);
+            }
+            treeIndexSortIndexMap.put(treeIndex, treeIndexSortIndexMapTmp.get(treeIndex));
+            lastTreeIndex = treeIndex;
+        }
+
+        //get taxonID for treeIndexes
+        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(treeIndexSortIndexMap.keySet());
+
+        //fill result list
+        for (UUID originalTaxonUuid : originalTaxonUuids){
+            GroupedTaxonDTO item = new GroupedTaxonDTO();
+            result.add(item);
+            item.setTaxonUuid(originalTaxonUuid);
+            TreeIndex groupTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
+            String groupIndexX = TreeIndex.toString(groupTreeIndex);
+            while (groupTreeIndex != null){
+                if (treeIndexTaxonIdMap.get(groupTreeIndex) != null){
+                    UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
+                    item.setGroupTaxonUuid(uuidAndLabel.getUuid());
+                    item.setGroupTaxonName(uuidAndLabel.getTitleCache());
+                    break;
+                }else{
+                    groupTreeIndex = groupTreeIndex.parent();
+//                    int index = groupIndex.substring(0, groupIndex.length()-1).lastIndexOf(ITreeNode.separator);
+//                    groupIndex = index < 0 ? null : groupIndex.substring(0, index+1);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
+            MarkerType markerType, Boolean flag) {
+
+        List<GroupedTaxonDTO> result = new ArrayList<>();
+
+        //get treeindex for each taxonUUID
+        Map<UUID, TreeIndex> taxonIdTreeIndexMap = dao.treeIndexForTaxonUuids(classificationUuid, originalTaxonUuids);
+
+        //get all marked tree indexes
+        Set<TreeIndex> markedTreeIndexes = dao.getMarkedTreeIndexes(markerType, flag);
+
+
+        Map<TreeIndex, TreeIndex> groupedMap = TreeIndex.group(markedTreeIndexes, taxonIdTreeIndexMap.values());
+        Set<TreeIndex> notNullGroups = new HashSet<>(groupedMap.values());
+        notNullGroups.remove(null);
+
+        //get taxonInfo for treeIndexes
+        Map<TreeIndex, UuidAndTitleCache<?>> treeIndexTaxonIdMap = taxonNodeDao.taxonUuidsForTreeIndexes(notNullGroups);
+
+        //fill result list
+        for (UUID originalTaxonUuid : originalTaxonUuids){
+            GroupedTaxonDTO item = new GroupedTaxonDTO();
+            result.add(item);
+            item.setTaxonUuid(originalTaxonUuid);
+
+            TreeIndex toBeGroupedTreeIndex = taxonIdTreeIndexMap.get(originalTaxonUuid);
+            TreeIndex groupTreeIndex = groupedMap.get(toBeGroupedTreeIndex);
+            UuidAndTitleCache<?> uuidAndLabel = treeIndexTaxonIdMap.get(groupTreeIndex);
+            if (uuidAndLabel != null){
+                item.setGroupTaxonUuid(uuidAndLabel.getUuid());
+                item.setGroupTaxonName(uuidAndLabel.getTitleCache());
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
+        Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
+        UUID taxonNodeUuid = map.get(taxonUuid);
+        return taxonNodeUuid;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
+            Boolean doChildren, Boolean doSynonyms, List<UUID> ancestorMarkers,
+            NodeSortMode sortMode) {
+        TaxonInContextDTO result = new TaxonInContextDTO();
+
+        TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
+        if (taxonBase == null){
+            throw new EntityNotFoundException("Taxon with uuid " + taxonBaseUuid + " not found in datasource");
+        }
+        boolean isSynonym = false;
+        Taxon acceptedTaxon;
+        if (taxonBase.isInstanceOf(Synonym.class)){
+            isSynonym = true;
+            Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
+            acceptedTaxon = synonym.getAcceptedTaxon();
+            if (acceptedTaxon == null) {
+                throw new EntityNotFoundException("Accepted taxon not found for synonym"  );
+            }
+            TaxonStatus taxonStatus = TaxonStatus.Synonym;
+            if (synonym.getName()!= null && acceptedTaxon.getName() != null
+                    && synonym.getName().getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup())){
+                taxonStatus = TaxonStatus.SynonymObjective;
+            }
+            result.setTaxonStatus(taxonStatus);
+
+        }else{
+            acceptedTaxon = CdmBase.deproxy(taxonBase, Taxon.class);
+            result.setTaxonStatus(TaxonStatus.Accepted);
+        }
+        UUID acceptedTaxonUuid = acceptedTaxon.getUuid();
+
+        UUID taxonNodeUuid = getTaxonNodeUuidByTaxonUuid(classificationUuid, acceptedTaxonUuid);
+        if (taxonNodeUuid == null) {
+            throw new EntityNotFoundException("Taxon not found in classficiation with uuid " + classificationUuid + ". Either classification does not exist or does not contain taxon/synonym with uuid " + taxonBaseUuid );
+        }
+        result.setTaxonNodeUuid(taxonNodeUuid);
+
+        //TODO make it a dao call
+        Taxon parentTaxon = getParentTaxon(classificationUuid, acceptedTaxon);
+        if (parentTaxon != null){
+            result.setParentTaxonUuid(parentTaxon.getUuid());
+            result.setParentTaxonLabel(parentTaxon.getTitleCache());
+            if (parentTaxon.getName() != null){
+                result.setParentNameLabel(parentTaxon.getName().getTitleCache());
+            }
+        }
+
+
+        result.setTaxonUuid(taxonBaseUuid);
+        result.setClassificationUuid(classificationUuid);
+        if (taxonBase.getSec() != null){
+            result.setSecundumUuid(taxonBase.getSec().getUuid());
+            result.setSecundumLabel(taxonBase.getSec().getTitleCache());
+        }
+        result.setTaxonLabel(taxonBase.getTitleCache());
+
+        TaxonName name = taxonBase.getName();
+        result.setNameUuid(name.getUuid());
+        result.setNameLabel(name.getTitleCache());
+        result.setNameWithoutAuthor(name.getNameCache());
+        result.setGenusOrUninomial(name.getGenusOrUninomial());
+        result.setInfraGenericEpithet(name.getInfraGenericEpithet());
+        result.setSpeciesEpithet(name.getSpecificEpithet());
+        result.setInfraSpecificEpithet(name.getInfraSpecificEpithet());
+
+        result.setAuthorship(name.getAuthorshipCache());
+
+        Rank rank = name.getRank();
+        if (rank != null){
+            result.setRankUuid(rank.getUuid());
+            String rankLabel = rank.getAbbreviation();
+            if (StringUtils.isBlank(rankLabel)){
+                rankLabel = rank.getLabel();
+            }
+            result.setRankLabel(rankLabel);
+        }
+
+        boolean recursive = false;
+        Integer pageSize = null;
+        Integer pageIndex = null;
+        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, doSynonyms, sortMode, pageSize, pageIndex);
+
+        //children
+        if(! isSynonym) {
+            for (TaxonNodeDto childDto : children.getRecords()){
+                if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
+                    EntityDTO<Taxon> child = new EntityDTO<Taxon>(childDto.getTaxonUuid(), childDto.getTitleCache());
+                    result.addChild(child);
+                }else if (doSynonyms && childDto.getStatus().isSynonym()){
+                    EntityDTO<Synonym> child = new EntityDTO<Synonym>(childDto.getTaxonUuid(), childDto.getTitleCache());
+                    result.addSynonym(child);
+                }
+            }
+        }else{
+            result.setAcceptedTaxonUuid(acceptedTaxonUuid);
+            String nameTitel = acceptedTaxon.getName() == null ? null : acceptedTaxon.getName().getTitleCache();
+            result.setAcceptedTaxonLabel(acceptedTaxon.getTitleCache());
+            result.setAcceptedNameLabel(nameTitel);
+        }
+
+        //marked ancestors
+        if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
+            List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
+            List<MarkerType> markerTypes = new ArrayList<>();
+            for (DefinedTermBase<?> term : markerTypesTerms){
+                if (term.isInstanceOf(MarkerType.class)){
+                    markerTypes.add(CdmBase.deproxy(term, MarkerType.class));
+                }
+            }
+            if (! markerTypes.isEmpty()){
+                TaxonNode node = taxonNodeDao.findByUuid(taxonNodeUuid);
+                handleAncestorsForMarkersRecursive(result, markerTypes, node);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * @param classificationUuid
+     * @param acceptedTaxon
+     * @return
+     */
+    private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
+        if (classificationUuid == null){
+            return null;
+        }
+        TaxonNode parent = null;
+        for (TaxonNode node : acceptedTaxon.getTaxonNodes()){
+            if (classificationUuid.equals(node.getClassification().getUuid())){
+                parent = node.getParent();
+            }
+        }
+        if (parent != null){
+            return parent.getTaxon();
+        }
+        return null;
+    }
+
+    /**
+     * @param result
+     * @param markerTypes
+     * @param node
+     */
+    private void handleAncestorsForMarkersRecursive(TaxonInContextDTO result, List<MarkerType> markerTypes, TaxonNode node) {
+       for (MarkerType type : markerTypes){
+            Taxon taxon = node.getTaxon();
+            if (taxon != null && taxon.hasMarker(type, true)){
+                String label =  taxon.getName() == null? taxon.getTitleCache() : taxon.getName().getTitleCache();
+                MarkedEntityDTO<Taxon> dto = new MarkedEntityDTO<>(type, true, taxon.getUuid(), label);
+                result.addMarkedAncestor(dto);
+            }
+        }
+        TaxonNode parentNode = node.getParent();
+        if (parentNode != null){
+            handleAncestorsForMarkersRecursive(result, markerTypes, parentNode);
+        }
+    }
+
+
 }