remove unused property path
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / ClassificationServiceImpl.java
index b89059af9d37143b1456519f0d172bc1f2e73f2f..496ace11c7fd47687d161d64d545b9bf1151d536 100644 (file)
@@ -6,7 +6,6 @@
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * See LICENSE.TXT at the top of this package for the full license terms.
 */
-
 package eu.etaxonomy.cdm.api.service;
 
 import java.util.ArrayList;
@@ -21,12 +20,14 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityNotFoundException;
 
 import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -34,7 +35,6 @@ 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;
@@ -43,10 +43,11 @@ 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.compare.taxon.ITaxonNodeComparator;
+import eu.etaxonomy.cdm.compare.taxon.TaxonNodeSortMode;
+import eu.etaxonomy.cdm.exception.FilterException;
+import eu.etaxonomy.cdm.exception.UnpublishedException;
 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;
@@ -58,21 +59,20 @@ import eu.etaxonomy.cdm.model.media.MediaUtils;
 import eu.etaxonomy.cdm.model.name.INonViralName;
 import eu.etaxonomy.cdm.model.name.Rank;
 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.model.term.DefinedTermBase;
 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.dao.term.IDefinedTermDao;
 import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
+import eu.etaxonomy.cdm.persistence.dto.EntityDTO;
 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
 import eu.etaxonomy.cdm.persistence.dto.TaxonStatus;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
@@ -86,9 +86,11 @@ import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
  */
 @Service
 @Transactional(readOnly = true)
-public class ClassificationServiceImpl extends IdentifiableServiceBase<Classification, IClassificationDao>
-    implements IClassificationService {
-    private static final Logger logger = Logger.getLogger(ClassificationServiceImpl.class);
+public class ClassificationServiceImpl
+             extends IdentifiableServiceBase<Classification, IClassificationDao>
+             implements IClassificationService {
+
+    private static final Logger logger = LogManager.getLogger();
 
     @Autowired
     private ITaxonNodeDao taxonNodeDao;
@@ -111,11 +113,11 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         this.dao = dao;
     }
 
-    private Comparator<? super TaxonNode> taxonNodeComparator;
+    private Comparator<TaxonNode> taxonNodeComparator;
 
     @Autowired
-    public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){
-        this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;
+    public void setTaxonNodeComparator(ITaxonNodeComparator<TaxonNode> taxonNodeComparator){
+        this.taxonNodeComparator = (Comparator<TaxonNode>) taxonNodeComparator;
     }
 
     @Override
@@ -126,76 +128,38 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return loadTaxonNode(node.getUuid(), propertyPaths);
     }
 
-    @Override
-    @Deprecated // use loadTaxonNode(UUID, List<String>) instead
-    public TaxonNode loadTaxonNode(TaxonNode taxonNode, List<String> propertyPaths){
-        return taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
-    }
-
     public TaxonNode loadTaxonNode(UUID taxonNodeUuid, List<String> propertyPaths){
         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;
+    public List<TaxonNode> listRankSpecificRootNodes(Classification classification,
+            TaxonNode subtree, Rank rank,
+            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
+        return pageRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths).getRecords();
     }
 
-    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<TaxonNodeDto> listRankSpecificRootNodeDtos(Classification classification, TaxonNode subtree,
+            Rank rank, boolean includeUnpublished, Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode,
+            List<String> propertyPaths) {
+        List<TaxonNode> list = listRankSpecificRootNodes(classification, subtree, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
+        return list.stream().filter(e ->  e != null).map(e -> new TaxonNodeDto(e)).sorted(sortMode.comparator()).collect(Collectors.toList());
     }
 
     @Override
-    public List<TaxonNode> listRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
-            Integer pageIndex, List<String> propertyPaths) {
-        return pageRankSpecificRootNodes(classification, rank, pageSize, pageIndex, propertyPaths).getRecords();
+    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank,
+            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
+        return pageRankSpecificRootNodes(classification, null, rank, includeUnpublished, pageSize, pageIndex, propertyPaths);
     }
 
     @Override
-    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, Rank rank, Integer pageSize,
-            Integer pageIndex, List<String> propertyPaths) {
-        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, rank);
+    public Pager<TaxonNode> pageRankSpecificRootNodes(Classification classification, TaxonNode subtree, Rank rank,
+            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
+        long[] numberOfResults = dao.countRankSpecificRootNodes(classification, subtree, includeUnpublished, rank);
         long totalNumberOfResults = numberOfResults[0] + (numberOfResults.length > 1 ? numberOfResults[1] : 0);
 
-        List<TaxonNode> results = new ArrayList<TaxonNode>();
+        List<TaxonNode> results = new ArrayList<>();
 
         if (AbstractPagerImpl.hasResultsInRange(totalNumberOfResults, pageIndex, pageSize)) { // no point checking again
             Integer limit = PagerUtils.limitFor(pageSize);
@@ -211,7 +175,9 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                     continue;
                 }
 
-                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification, rank, remainingLimit, start, propertyPaths, queryIndex);
+                List<TaxonNode> perQueryResults = dao.listRankSpecificRootNodes(classification,
+                        subtree, rank, includeUnpublished, remainingLimit,
+                        start, propertyPaths, queryIndex);
                 results.addAll(perQueryResults);
                 if(remainingLimit != null ){
                     remainingLimit = remainingLimit - results.size();
@@ -227,47 +193,58 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
 //        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);
+        return new DefaultPagerImpl<>(pageIndex, totalNumberOfResults, pageSize, results);
 
     }
 
-    /**
-     * @implements {@link IClassificationService#loadTreeBranch(TaxonNode, Rank, List)
-     * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)
-     * FIXME Candidate for harmonization
-     * move to classification service
-     */
     @Override
-    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){
+    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, Rank baseRank,
+            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
+        return loadTreeBranch(taxonNode, null, baseRank, includeUnpublished, propertyPaths);
+    }
+
+    @Override
+    public List<TaxonNode> loadTreeBranch(TaxonNode taxonNode, TaxonNode subtree, Rank baseRank,
+            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
 
         TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);
         if(baseRank != null){
             baseRank = (Rank) termDao.load(baseRank.getUuid());
         }
-        List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();
+        if (!includeUnpublished && thisNode.getTaxon() != null && !thisNode.getTaxon().isPublish()){
+            throw new UnpublishedException("Final taxon in tree branch is unpublished.");
+        }
+
+        List<TaxonNode> pathToRoot = new ArrayList<>();
         pathToRoot.add(thisNode);
 
         while(!thisNode.isTopmostNode()){
             //TODO why do we need to deproxy here?
             //     without this thisNode.getParent() will return NULL in
             //     some cases (environment dependend?) even if the parent exits
-            TaxonNode parentNode = CdmBase.deproxy(thisNode, TaxonNode.class).getParent();
+            TaxonNode parentNode = CdmBase.deproxy(thisNode).getParent();
 
             if(parentNode == null){
-                throw new NullPointerException("taxonNode " + thisNode + " must have a parent since it is not top most");
+                throw new NullPointerException("Taxon node " + thisNode + " must have a parent since it is not top most");
             }
             if(parentNode.getTaxon() == null){
-                throw new NullPointerException("The taxon associated with taxonNode " + parentNode + " is NULL");
+                throw new NullPointerException("The taxon associated with taxon node " + parentNode + " is NULL");
+            }
+            if(!includeUnpublished && !parentNode.getTaxon().isPublish()){
+                throw new UnpublishedException("Some taxon in tree branch is unpublished.");
             }
             if(parentNode.getTaxon().getName() == null){
                 throw new NullPointerException("The name of the taxon associated with taxonNode " + parentNode + " is NULL");
             }
 
-            Rank parentNodeRank = parentNode.getTaxon().getName() == null ? null : parentNode.getTaxon().getName().getRank();
+            Rank parentNodeRank = (parentNode.getTaxon().getName() == null) ? null : parentNode.getTaxon().getName().getRank();
             // stop if the next parent is higher than the baseRank
             if(baseRank != null && parentNodeRank != null && baseRank.isLower(parentNodeRank)){
                 break;
             }
+            if((subtree!= null && !subtree.isAncestor(parentNode) )){
+                break;
+            }
 
             pathToRoot.add(parentNode);
             thisNode = parentNode;
@@ -281,15 +258,36 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     }
 
     @Override
-    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank, List<String> propertyPaths){
-        Classification tree = dao.load(classification.getUuid());
-        taxon = (Taxon) taxonDao.load(taxon.getUuid());
-        TaxonNode node = tree.getNode(taxon);
+    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification, Rank baseRank,
+            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
+        return loadTreeBranchToTaxon(taxon, classification, null, baseRank, includeUnpublished, propertyPaths);
+    }
+
+    @Override
+    public List<TaxonNodeDto> loadTreeBranchDTOsToTaxon(Taxon taxon, Classification classification,
+            TaxonNode subtree, Rank baseRank,
+            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException {
+        List<TaxonNode> list = loadTreeBranchToTaxon(taxon, classification, subtree, baseRank, includeUnpublished, propertyPaths);
+        return list.stream().map(e -> new TaxonNodeDto(e)).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, Classification classification,
+            TaxonNode subtree, Rank baseRank,
+            boolean includeUnpublished, List<String> propertyPaths) throws UnpublishedException{
+
+        UUID nodeUuid = getTaxonNodeUuidByTaxonUuid(classification.getUuid(), taxon.getUuid());
+        TaxonNode node = taxonNodeService.find(nodeUuid);
         if(node == null){
             logger.warn("The specified taxon is not found in the given tree.");
-            return null;
+            return new ArrayList<>(0);
+        }else if (subtree != null && !node.isDescendant(subtree)){
+            //TODO handle as exception? E.g. FilterException, AccessDeniedException?
+            logger.warn("The specified taxon is not found for the given subtree.");
+            return new ArrayList<>(0);
         }
-        return loadTreeBranch(node, baseRank, propertyPaths);
+
+        return loadTreeBranch(node, subtree, baseRank, includeUnpublished, propertyPaths);
     }
 
 
@@ -304,50 +302,83 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     }
 
     @Override
-    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
-            Integer pageIndex, List<String> propertyPaths){
+    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid,
+            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths){
+        try {
+            return listChildNodesOfTaxon(taxonUuid, classificationUuid, null, includeUnpublished, pageSize, pageIndex, propertyPaths);
+        } catch (FilterException e) {
+            throw new RuntimeException(e);  //this should not happen as filter is null
+        }
+    }
+
+    @Override
+    public List<TaxonNode> listChildNodesOfTaxon(UUID taxonUuid, UUID classificationUuid, UUID subtreeUuid,
+            boolean includeUnpublished, Integer pageSize, Integer pageIndex, List<String> propertyPaths) throws FilterException{
 
         Classification classification = dao.load(classificationUuid);
         Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
+        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
+        if (subtreeUuid != null && subtree == null){
+            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
+        }
 
-        List<TaxonNode> results = dao.listChildrenOf(taxon, classification, pageSize, pageIndex, propertyPaths);
+        List<TaxonNode> results = dao.listChildrenOf(
+                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
         Collections.sort(results, taxonNodeComparator); // FIXME this is only a HACK, order during the hibernate query in the dao
         return results;
     }
 
     @Override
-    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
-            Integer pageIndex, List<String> propertyPaths){
+    public List<TaxonNodeDto> listChildNodeDtosOfTaxon(UUID taxonUuid, UUID classificationUuid,
+            UUID subtreeUuid, boolean includeUnpublished,
+            Integer pageSize, Integer pageIndex, TaxonNodeDtoSortMode sortMode) throws FilterException{
 
         Classification classification = dao.load(classificationUuid);
         Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
+        TaxonNode subtree = taxonNodeDao.load(subtreeUuid);
+        if (subtreeUuid != null && subtree == null){
+            throw new FilterException("Taxon node for subtree filter can not be found in database", true);
+        }
 
-        long numberOfResults = dao.countSiblingsOf(taxon, classification);
+        List<TaxonNode> results = dao.listChildrenOf(
+                taxon, classification, subtree, includeUnpublished, pageSize, pageIndex, propertyPaths);
+        Comparator<TaxonNodeDto> comparator = sortMode.comparator();
+        // TODO order during the hibernate query in the dao?
+        List<TaxonNodeDto> dtos = results.stream()
+                .map(tn -> new TaxonNodeDto(tn))
+                .sorted(comparator)
+                .collect(Collectors.toList());
+        return dtos;
+    }
+
+    @Override
+    public Pager<TaxonNode> pageSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
+            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
+
+        Classification classification = dao.load(classificationUuid);
+        Taxon taxon = (Taxon) taxonDao.load(taxonUuid);
+
+        long numberOfResults = dao.countSiblingsOf(taxon, classification, includeUnpublished);
 
         List<TaxonNode> results;
         if(PagerUtils.hasResultsInRange(numberOfResults, pageIndex, pageSize)) {
-            results = dao.listSiblingsOf(taxon, classification, pageSize, pageIndex, propertyPaths);
+            results = dao.listSiblingsOf(taxon, classification, includeUnpublished, 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);
+        return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
     }
 
     @Override
-    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, Integer pageSize,
-            Integer pageIndex, List<String> propertyPaths){
+    public List<TaxonNode> listSiblingsOfTaxon(UUID taxonUuid, UUID classificationUuid, boolean includeUnpublished,
+            Integer pageSize, Integer pageIndex, List<String> propertyPaths){
 
-        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, pageSize, pageIndex, propertyPaths);
+        Pager<TaxonNode> pager = pageSiblingsOfTaxon(taxonUuid, classificationUuid, includeUnpublished, pageSize, pageIndex, propertyPaths);
         return pager.getRecords();
     }
 
-    @Override
-    public TaxonNode getTaxonNodeByUuid(UUID uuid) {
-        return taxonNodeDao.findByUuid(uuid);
-    }
-
     @Override
     public ITaxonTreeNode getTreeNodeByUuid(UUID uuid){
         ITaxonTreeNode treeNode = taxonNodeDao.findByUuid(uuid);
@@ -368,10 +399,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return dao.list(limit, start, orderHints, propertyPaths);
     }
 
-    @Override
-    public UUID removeTaxonNode(TaxonNode taxonNode) {
-        return taxonNodeDao.delete(taxonNode);
-    }
     @Override
     public UUID removeTreeNode(ITaxonTreeNode treeNode) {
         if(treeNode instanceof Classification){
@@ -381,10 +408,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         }
         return null;
     }
-    @Override
-    public UUID saveTaxonNode(TaxonNode taxonNode) {
-        return taxonNodeDao.save(taxonNode).getUuid();
-    }
 
     @Override
     public Map<UUID, TaxonNode> saveTaxonNodeAll(
@@ -416,8 +439,8 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
     }
 
     @Override
-    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
-        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications);
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications, boolean includeDoubtful) {
+        return taxonNodeDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(dao.load(classificationUuid),  limit, pattern, searchForClassifications, includeDoubtful);
     }
 
     @Override
@@ -445,51 +468,41 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
             TaxonNode taxonNode, List<String> propertyPaths, int size,
             int height, int widthOrDuration, String[] mimeTypes) {
 
-        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();
-        List<Media> taxonMedia = new ArrayList<Media>();
-        List<MediaRepresentation> mediaRepresentations = new ArrayList<MediaRepresentation>();
+        TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<>();
+        List<MediaRepresentation> mediaRepresentations = new ArrayList<>();
 
         //add all media of the children to the result map
         if (taxonNode != null){
 
-            List<TaxonNode> nodes = new ArrayList<TaxonNode>();
+            List<TaxonNode> nodes = new ArrayList<>();
 
-            nodes.add(loadTaxonNode(taxonNode, propertyPaths));
+            nodes.add(loadTaxonNode(taxonNode.getUuid(), propertyPaths));
             nodes.addAll(loadChildNodesOfTaxonNode(taxonNode, propertyPaths));
 
-            if (nodes != null){
-                for(TaxonNode node : nodes){
-                    Taxon taxon = node.getTaxon();
-                    for (TaxonDescription taxonDescription: taxon.getDescriptions()){
-                        for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
-                            for(Media media : descriptionElement.getMedia()){
-                                taxonMedia.add(media);
-
-                                //find the best matching representation
-                                mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes));
-
-                            }
+            for(TaxonNode node : nodes){
+                Taxon taxon = node.getTaxon();
+                for (TaxonDescription taxonDescription: taxon.getDescriptions()){
+                    for (DescriptionElementBase descriptionElement: taxonDescription.getElements()){
+                        for(Media media : descriptionElement.getMedia()){
+                            //find the best matching representation
+                            mediaRepresentations.add(MediaUtils.findBestMatchingRepresentation(media,null, size, height, widthOrDuration, mimeTypes, MediaUtils.MissingValueStrategy.MAX));
                         }
                     }
-                    result.put(taxon.getUuid(), mediaRepresentations);
-
                 }
+                result.put(taxon.getUuid(), mediaRepresentations);
             }
-
         }
 
         return result;
-
     }
 
-
     @Override
     @Transactional(readOnly = false)
-    public void updateTitleCache(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
+    public UpdateResult updateCaches(Class<? extends Classification> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<Classification> cacheStrategy, IProgressMonitor monitor) {
         if (clazz == null){
             clazz = Classification.class;
         }
-        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
     }
 
     /**
@@ -505,7 +518,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
        }
        Map<String, List<TaxonNode>> sortedGenusMap = new HashMap<>();
        for(TaxonNode node:allNodesOfClassification){
-               final TaxonNode tn = node;
                Taxon taxon = node.getTaxon();
                INonViralName name = taxon.getName();
                String genusOrUninomial = name.getGenusOrUninomial();
@@ -527,7 +539,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                        sortedGenusMap.put(genusOrUninomial, list);
                }else{
                        //create List for genus
-                       List<TaxonNode> list = new ArrayList<TaxonNode>();
+                       List<TaxonNode> list = new ArrayList<>();
                        list.add(node);
                        sortedGenusMap.put(genusOrUninomial, list);
                }
@@ -549,7 +561,10 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
        @Transactional(readOnly = false)
        @Override
     public UpdateResult createHierarchyInClassification(Classification classification, CreateHierarchyForClassificationConfigurator configurator){
+
         UpdateResult result = new UpdateResult();
+        Set<TaxonNode> taxonNodesToSave = new HashSet<>();
+
        classification = dao.findByUuid(classification.getUuid());
        Map<String, List<TaxonNode>> map = getSortedGenusList(classification.getAllNodes());
 
@@ -569,7 +584,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                        //FIXME NPE for name
                        TaxonName name = tNode.getTaxon().getName();
                        if(name.getNameCache().equalsIgnoreCase(genus)){
-                               TaxonNode clone = (TaxonNode) tNode.clone();
+                               TaxonNode clone = tNode.clone();
                                if(!tNode.hasChildNodes()){
                                        //FIXME remove classification
 //                                     parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
@@ -581,8 +596,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                                        //get all childNodes
                                        //save prior Hierarchy and remove them from the list
                                        List<TaxonNode> copyAllChildrenToTaxonNode = copyAllChildrenToTaxonNode(tNode, clone, result);
-//                                     parentNode = newClassification.addChildNode(clone, 0, classification.getCitation(), classification.getMicroReference());
-                                       //FIXME remove classification
+//                                     //FIXME remove classification
                                        parentNode = newClassification.addChildNode(clone, 0, clone.getReference(), clone.getMicroReference());
                                        //remove taxonNode from list because just added to classification
                                        result.addUpdatedObject(tNode);
@@ -603,7 +617,9 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                        parentNode = newClassification.addChildTaxon(taxon, 0, null, null);
                        result.addUpdatedObject(parentNode);
                }
-               //iterate over the rest of the list
+               taxonNodesToSave.add(parentNode);
+
+               //iterate over the remaining list
                for(TaxonNode tn : listOfTaxonNodes){
                        //if TaxonNode has a parent and this is not the classification then skip it
                        //and add to new classification via the parentNode as children of it
@@ -614,11 +630,12 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                                continue; //skip to next taxonNode
                        }
 
-                       TaxonNode clone = (TaxonNode) tn.clone();
+                       TaxonNode clone = tn.clone();
                        //FIXME: citation from node
-                       //TODO: addchildNode without citation and references
-//                     TaxonNode taxonNode = parentNode.addChildNode(clone, classification.getCitation(), classification.getMicroReference());
+                       //TODO: addChildNode without citation and references
                        TaxonNode taxonNode = parentNode.addChildNode(clone, clone.getReference(), clone.getMicroReference());
+                       taxonNodesToSave.add(taxonNode);
+
                        result.addUnChangedObject(clone);
                        if(tn.hasChildNodes()){
                                //save hierarchy in new classification
@@ -630,6 +647,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                }
        }
        dao.saveOrUpdate(newClassification);
+       taxonNodeDao.saveOrUpdateAll(taxonNodesToSave);
        result.setCdmEntity(newClassification);
        return result;
     }
@@ -651,7 +669,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
                        childNodes = copyFromNode.getChildNodes();
                }
                for(TaxonNode childNode:childNodes){
-                       TaxonNode clone = (TaxonNode) childNode.clone();
+                       TaxonNode clone = childNode.clone();
                        result.addUnChangedObject(clone);
                        if(childNode.hasChildNodes()){
                                copyAllChildrenToTaxonNode(childNode, clone, result);
@@ -663,15 +681,11 @@ 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){
@@ -771,9 +785,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return result;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public List<GroupedTaxonDTO> groupTaxaByMarkedParents(List<UUID> originalTaxonUuids, UUID classificationUuid,
             MarkerType markerType, Boolean flag) {
@@ -786,7 +797,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         //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);
@@ -812,9 +822,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return result;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public UUID getTaxonNodeUuidByTaxonUuid(UUID classificationUuid, UUID taxonUuid) {
         Map<UUID, UUID> map = dao.getTaxonNodeUuidByTaxonUuid(classificationUuid, Arrays.asList(taxonUuid));
@@ -822,13 +829,11 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return taxonNodeUuid;
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public TaxonInContextDTO getTaxonInContext(UUID classificationUuid, UUID taxonBaseUuid,
-            Boolean doChildren, Boolean doSynonyms, List<UUID> ancestorMarkers,
-            NodeSortMode sortMode) {
+            Boolean doChildren, Boolean doSynonyms, boolean includeUnpublished, List<UUID> ancestorMarkers,
+            TaxonNodeSortMode sortMode) {
+
         TaxonInContextDTO result = new TaxonInContextDTO();
 
         TaxonBase<?> taxonBase = taxonDao.load(taxonBaseUuid);
@@ -873,7 +878,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
             }
         }
 
-
         result.setTaxonUuid(taxonBaseUuid);
         result.setClassificationUuid(classificationUuid);
         if (taxonBase.getSec() != null){
@@ -906,16 +910,17 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         boolean recursive = false;
         Integer pageSize = null;
         Integer pageIndex = null;
-        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, doSynonyms, sortMode, pageSize, pageIndex);
+        Pager<TaxonNodeDto> children = taxonNodeService.pageChildNodesDTOs(taxonNodeUuid, recursive, includeUnpublished, doSynonyms,
+                sortMode, pageSize, pageIndex);
 
         //children
         if(! isSynonym) {
             for (TaxonNodeDto childDto : children.getRecords()){
-                if (doChildren && childDto.getStatus().equals(TaxonStatus.Accepted)){
+                if (doChildren && childDto.getTaxonStatus().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());
+                }else if (doSynonyms && childDto.getTaxonStatus().isSynonym()){
+                    EntityDTO<Synonym> child = new EntityDTO<>(childDto.getTaxonUuid(), childDto.getTitleCache());
                     result.addSynonym(child);
                 }
             }
@@ -928,6 +933,7 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
 
         //marked ancestors
         if (ancestorMarkers != null && !ancestorMarkers.isEmpty()){
+            @SuppressWarnings("rawtypes")
             List<DefinedTermBase> markerTypesTerms = termDao.list(ancestorMarkers, pageSize, null, null, null);
             List<MarkerType> markerTypes = new ArrayList<>();
             for (DefinedTermBase<?> term : markerTypesTerms){
@@ -944,11 +950,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         return result;
     }
 
-    /**
-     * @param classificationUuid
-     * @param acceptedTaxon
-     * @return
-     */
     private Taxon getParentTaxon(UUID classificationUuid, Taxon acceptedTaxon) {
         if (classificationUuid == null){
             return null;
@@ -965,11 +966,6 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         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();
@@ -985,41 +981,34 @@ public class ClassificationServiceImpl extends IdentifiableServiceBase<Classific
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
             Classification classification) {
         return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, false);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
             UUID classificationUuid) {
         return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid, false);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
             UUID classificationUuid, Integer limit, String pattern) {
         return  getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classificationUuid,  limit, pattern, false);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
             Classification classification, Integer limit, String pattern) {
         return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, false);
     }
 
-
+    @Override
+    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
+            UUID classificationUuid, Integer limit, String pattern, boolean searchForClassifications) {
+        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
+                classificationUuid, limit, pattern, searchForClassifications, false);
+    }
 }