cleanup
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index 9769db2b7906c9222740d4e58c7458b9b4387067..09ea57c2642a491ad89657ffad6c62ddf2425b4a 100644 (file)
@@ -11,6 +11,7 @@ package eu.etaxonomy.cdm.api.service;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -19,6 +20,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityNotFoundException;
 
@@ -33,6 +35,7 @@ import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.grouping.TopGroups;
 import org.apache.lucene.search.join.ScoreMode;
 import org.apache.lucene.util.BytesRef;
+import org.hibernate.criterion.Criterion;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -63,8 +66,10 @@ import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
 import eu.etaxonomy.cdm.api.service.search.SearchResult;
 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
-import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
+import eu.etaxonomy.cdm.api.util.TaxonRelationshipEdge;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+import eu.etaxonomy.cdm.compare.taxon.HomotypicGroupTaxonComparator;
+import eu.etaxonomy.cdm.compare.taxon.TaxonComparator;
 import eu.etaxonomy.cdm.exception.UnpublishedException;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
@@ -81,6 +86,7 @@ import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
 import eu.etaxonomy.cdm.model.description.DescriptionBase;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
 import eu.etaxonomy.cdm.model.description.Distribution;
 import eu.etaxonomy.cdm.model.description.Feature;
 import eu.etaxonomy.cdm.model.description.IIdentificationKey;
@@ -91,7 +97,11 @@ import eu.etaxonomy.cdm.model.description.TaxonDescription;
 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
 import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.media.ExternalLink;
+import eu.etaxonomy.cdm.model.media.ExternalLinkType;
 import eu.etaxonomy.cdm.model.media.Media;
+import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingEnum;
+import eu.etaxonomy.cdm.model.metadata.SecReferenceHandlingSwapEnum;
 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
 import eu.etaxonomy.cdm.model.name.IZoologicalName;
 import eu.etaxonomy.cdm.model.name.Rank;
@@ -103,8 +113,8 @@ import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
 import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.taxon.Classification;
-import eu.etaxonomy.cdm.model.taxon.HomotypicGroupTaxonComparator;
 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
+import eu.etaxonomy.cdm.model.taxon.SecundumSource;
 import eu.etaxonomy.cdm.model.taxon.Synonym;
 import eu.etaxonomy.cdm.model.taxon.SynonymType;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
@@ -120,6 +130,7 @@ import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
 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.MergeResult;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.persistence.query.MatchMode;
 import eu.etaxonomy.cdm.persistence.query.OrderHint;
@@ -163,6 +174,9 @@ public class TaxonServiceImpl
 
     @Autowired
     private IDescriptionService descriptionService;
+
+    @Autowired
+    private IReferenceService referenceService;
 //
 //    @Autowired
 //    private IOrderedTermVocabularyDao orderedVocabularyDao;
@@ -186,10 +200,6 @@ public class TaxonServiceImpl
 
 // ****************************** METHODS ********************************/
 
-
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public TaxonBase load(UUID uuid, boolean includeUnpublished, List<String> propertyPaths) {
         return dao.load(uuid, includeUnpublished, propertyPaths);
@@ -202,34 +212,202 @@ public class TaxonServiceImpl
 
     @Override
     @Transactional(readOnly = false)
-    public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
-       UpdateResult result = new UpdateResult();
-        TaxonName synonymName = synonym.getName();
-        synonymName.removeTaxonBase(synonym);
-        TaxonName taxonName = acceptedTaxon.getName();
-        taxonName.removeTaxonBase(acceptedTaxon);
-
-        synonym.setName(taxonName);
-        synonym.setTitleCache(null, false);
-        synonym.getTitleCache();
-        acceptedTaxon.setName(synonymName);
-        acceptedTaxon.setTitleCache(null, false);
-        acceptedTaxon.getTitleCache();
-        saveOrUpdate(synonym);
-        saveOrUpdate(acceptedTaxon);
-        result.addUpdatedObject(acceptedTaxon);
-        result.addUpdatedObject(synonym);
+    public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource, boolean newUuidForAcceptedTaxon, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
+        if (newUuidForAcceptedTaxon){
+            return swapSynonymAndAcceptedTaxonNewUuid(synonym, acceptedTaxon, setNameInSource, secHandling, newSecAcc, newSecSyn);
+        }else{
+            return swapSynonymAndAcceptedTaxon(synonym, acceptedTaxon, setNameInSource, secHandling, newSecAcc, newSecSyn);
+        }
+    }
+
+    private UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
+        UpdateResult result = new UpdateResult();
+        String oldTaxonTitleCache = acceptedTaxon.getTitleCache();
+
+       TaxonName synonymName = synonym.getName();
+       TaxonName taxonName = HibernateProxyHelper.deproxy(acceptedTaxon.getName());
+       Reference secAccepted = acceptedTaxon.getSec();
+       String microRefSecAccepted = acceptedTaxon.getSecMicroReference();
+       Reference secSynonym = synonym.getSec();
+        String microRefSecSynonym = synonym.getSecMicroReference();
+
+//        if (secHandling.equals(SecReferenceHandlingSwapEnum.AlwaysDelete) || (secAccepted != null && secSynonym != null && !secAccepted.getUuid().equals(secSynonym.getUuid()) && (secHandling.equals(SecReferenceHandlingSwapEnum.AlwaysSelect) || secHandling.equals(SecReferenceHandlingSwapEnum.KeepOrSelect)))){
+//            secAccepted = null;
+//            microRefSecAccepted = null;
+//            secSynonym = null;
+//            microRefSecSynonym = null;
+//        }
+
+
+       Set<ExternalLink> accLinks = new HashSet<>();
+       if (acceptedTaxon.getSecSource() != null){
+               for (ExternalLink link: acceptedTaxon.getSecSource().getLinks()){
+                accLinks.add(ExternalLink.NewInstance(ExternalLinkType.Unknown, link.getUri()));
+            }
+       }
+       acceptedTaxon.setName(synonymName);
+       acceptedTaxon.setSec(newSecAcc);
+//     acceptedTaxon.setSecMicroReference(synonym.getSecMicroReference());
+
+//     if (synonym.getSecSource()!= null && synonym.getSecSource().getLinks() != null){
+//         acceptedTaxon.getSecSource().getLinks().clear();
+//             for (ExternalLink link: synonym.getSecSource().getLinks()){
+//                 acceptedTaxon.getSecSource().addLink(ExternalLink.NewInstance(ExternalLinkType.Unknown, link.getUri()));
+//            }
+//     }
+
+       synonym.setName(taxonName);
+       synonym.setSec(newSecSyn);
+//        synonym.setSecMicroReference(microRefSecAccepted);
+//        if (synonym.getSecSource() != null){
+//            synonym.getSecSource().getLinks().clear();
+//            for (ExternalLink link: accLinks){
+//                synonym.getSecSource().addLink(link);
+//            }
+//        }
+
+       //nameUsedInSource
+       handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, acceptedTaxon.getDescriptions());
+
+       acceptedTaxon.resetTitleCache();
+       synonym.resetTitleCache();
+
+       MergeResult mergeTaxon = merge(acceptedTaxon, true);
+       MergeResult mergeSynonym = merge(synonym, true);
+       result.setCdmEntity((CdmBase) mergeTaxon.getMergedEntity());
+       result.addUpdatedObject((CdmBase) mergeSynonym.getMergedEntity());
+
                return result;
+    }
+
+    private UpdateResult swapSynonymAndAcceptedTaxonNewUuid(Synonym oldSynonym, Taxon oldAcceptedTaxon, boolean setNameInSource, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
+        UpdateResult result = new UpdateResult();
+        oldAcceptedTaxon.removeSynonym(oldSynonym);
+        TaxonName synonymName = oldSynonym.getName();
+        TaxonName taxonName = HibernateProxyHelper.deproxy(oldAcceptedTaxon.getName());
+        String oldTaxonTitleCache = oldAcceptedTaxon.getTitleCache();
+
+        boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(taxonName.getHomotypicalGroup());
+        synonymName.removeTaxonBase(oldSynonym);
+
+        List<Synonym> synonyms = new ArrayList<>();
+        for (Synonym syn: oldAcceptedTaxon.getSynonyms()){
+            syn = HibernateProxyHelper.deproxy(syn, Synonym.class);
+            synonyms.add(syn);
+        }
+        for (Synonym syn: synonyms){
+            oldAcceptedTaxon.removeSynonym(syn);
+        }
+        Taxon newTaxon = oldAcceptedTaxon.clone(true, true, false, true);
+        newTaxon.setSec(newSecAcc);
+
+        //move descriptions
+        Set<TaxonDescription> descriptionsToCopy = new HashSet<>(oldAcceptedTaxon.getDescriptions());
+        for (TaxonDescription description: descriptionsToCopy){
+            newTaxon.addDescription(description);
+        }
+        //nameUsedInSource
+        handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, newTaxon.getDescriptions());
+
+        newTaxon.setName(synonymName);
+
+        newTaxon.setPublish(oldSynonym.isPublish());
+        for (Synonym syn: synonyms){
+            if (!syn.getName().equals(newTaxon.getName())){
+                newTaxon.addSynonym(syn, syn.getType());
+            }
+        }
+
+        //move all data to new taxon
+        //Move Taxon RelationShips to new Taxon
+        for(TaxonRelationship taxonRelationship : newTaxon.getTaxonRelations()){
+            newTaxon.removeTaxonRelation(taxonRelationship);
+        }
+
+        for(TaxonRelationship taxonRelationship : oldAcceptedTaxon.getTaxonRelations()){
+            Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
+            Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
+            if (fromTaxon == oldAcceptedTaxon){
+                newTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
+                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
+
+            }else if(toTaxon == oldAcceptedTaxon){
+               fromTaxon.addTaxonRelation(newTaxon, taxonRelationship.getType(),
+                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
+               saveOrUpdate(fromTaxon);
 
-        // the accepted taxon needs a new uuid because the concept has changed
-        // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
-        //acceptedTaxon.setUuid(UUID.randomUUID());
+            }else{
+                logger.warn("Taxon is not part of its own Taxonrelationship");
+            }
+
+            // Remove old relationships
+            fromTaxon.removeTaxonRelation(taxonRelationship);
+            toTaxon.removeTaxonRelation(taxonRelationship);
+            taxonRelationship.setToTaxon(null);
+            taxonRelationship.setFromTaxon(null);
+        }
+
+        //taxon nodes
+        List<TaxonNode> nodes = new ArrayList<>(oldAcceptedTaxon.getTaxonNodes());
+        for (TaxonNode node: nodes){
+            node = HibernateProxyHelper.deproxy(node);
+            TaxonNode parent = node.getParent();
+            oldAcceptedTaxon.removeTaxonNode(node);
+            node.setTaxon(newTaxon);
+            if (parent != null){
+                parent.addChildNode(node, null, null);
+            }
+        }
+
+        //synonym
+        Synonym newSynonym = oldSynonym.clone();
+        newSynonym.setName(taxonName);
+        newSynonym.setPublish(oldAcceptedTaxon.isPublish());
+        newSynonym.setSec(newSecSyn);
+        if (sameHomotypicGroup){
+            newTaxon.addSynonym(newSynonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
+        }else{
+            newTaxon.addSynonym(newSynonym, SynonymType.HETEROTYPIC_SYNONYM_OF());
+        }
+
+        //deletes
+        TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
+        conf.setDeleteNameIfPossible(false);
+        SynonymDeletionConfigurator confSyn = new SynonymDeletionConfigurator();
+        confSyn.setDeleteNameIfPossible(false);
+        result.setCdmEntity(newTaxon);
+
+        DeleteResult deleteResult = deleteTaxon(oldAcceptedTaxon.getUuid(), conf, null);
+        if (oldSynonym.isPersited()){
+            oldSynonym.setSecSource(null);
+            deleteResult.includeResult(deleteSynonym(oldSynonym.getUuid(), confSyn));
+        }
+        result.includeResult(deleteResult);
+
+        return result;
     }
 
+    private void handleNameUsedInSourceForSwap(boolean setNameInSource, TaxonName taxonName, String oldTaxonTitleCache,
+            Set<TaxonDescription> descriptions) {
+        for(TaxonDescription description : descriptions){
+            String message = "Description copied from former accepted taxon: %s (Old title: %s)";
+            message = String.format(message, oldTaxonTitleCache, description.getTitleCache());
+            description.setTitleCache(message, true);
+            if(setNameInSource){
+                for (DescriptionElementBase element: description.getElements()){
+                    for (DescriptionElementSource source: element.getSources()){
+                        if (source.getNameUsedInSource() == null){
+                            source.setNameUsedInSource(taxonName);
+                        }
+                    }
+                }
+            }
+        }
+    }
 
     @Override
     @Transactional(readOnly = false)
-    public UpdateResult changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym) {
+    public UpdateResult changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, Reference newSecRef, String microRef, SecReferenceHandlingEnum secHandling, boolean deleteSynonym) {
         UpdateResult result = new UpdateResult();
         TaxonName acceptedName = acceptedTaxon.getName();
         TaxonName synonymName = synonym.getName();
@@ -243,13 +421,17 @@ public class TaxonServiceImpl
             return result;
         }
 
-        Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
+        Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, newSecRef, microRef);
+        newAcceptedTaxon.setPublish(synonym.isPublish());
         dao.save(newAcceptedTaxon);
         result.setCdmEntity(newAcceptedTaxon);
         SynonymType relTypeForGroup = SynonymType.HOMOTYPIC_SYNONYM_OF();
         List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
 
         for (Synonym heteroSynonym : heteroSynonyms){
+            if (secHandling == null){
+                heteroSynonym.setSec(newSecRef);
+            }
             if (synonym.equals(heteroSynonym)){
                 acceptedTaxon.removeSynonym(heteroSynonym, false);
             }else{
@@ -280,13 +462,41 @@ public class TaxonServiceImpl
     public UpdateResult changeSynonymToAcceptedTaxon(UUID synonymUuid,
             UUID acceptedTaxonUuid,
             UUID newParentNodeUuid,
+            UUID newSec,
+            String microReference,
+            SecReferenceHandlingEnum secHandling,
             boolean deleteSynonym)  {
         UpdateResult result = new UpdateResult();
         Synonym synonym = CdmBase.deproxy(dao.load(synonymUuid), Synonym.class);
         Taxon acceptedTaxon = CdmBase.deproxy(dao.load(acceptedTaxonUuid), Taxon.class);
-        result =  changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, deleteSynonym);
-        Taxon newTaxon = (Taxon)result.getCdmEntity();
         TaxonNode newParentNode = taxonNodeDao.load(newParentNodeUuid);
+        Reference newSecRef = null;
+        switch (secHandling){
+            case AlwaysDelete:
+                newSecRef = null;
+                break;
+            case UseNewParentSec:
+                newSecRef = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
+                break;
+            case KeepOrWarn:
+                Reference parentSec = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
+                Reference synSec = synonym.getSec();
+                if (synSec != null ){
+                    newSecRef = CdmBase.deproxy(synSec);
+                }else{
+                    newSecRef = CdmBase.deproxy(referenceService.load(newSec));
+                }
+                break;
+            case KeepOrSelect:
+                newSecRef = CdmBase.deproxy(referenceService.load(newSec));
+                break;
+            default:
+                break;
+        }
+
+        result =  changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, newSecRef, microReference, secHandling, deleteSynonym);
+        Taxon newTaxon = (Taxon)result.getCdmEntity();
+
         TaxonNode newNode = newParentNode.addChildTaxon(newTaxon, null, null);
         taxonNodeDao.save(newNode);
         result.addUpdatedObject(newTaxon);
@@ -295,9 +505,6 @@ public class TaxonServiceImpl
         return result;
     }
 
-
-
-
     @Override
     @Transactional(readOnly = false)
     public UpdateResult changeSynonymToRelatedTaxon(UUID synonymUuid,
@@ -334,6 +541,7 @@ public class TaxonServiceImpl
 */
         // Create a taxon with synonym name
         Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
+        fromTaxon.setPublish(synonym.isPublish());
         save(fromTaxon);
         fromTaxon.setAppendedPhrase(synonym.getAppendedPhrase());
 
@@ -393,7 +601,6 @@ public class TaxonServiceImpl
             SynonymType relType = isHomotypicToTaxon? SynonymType.HOMOTYPIC_SYNONYM_OF() : SynonymType.HETEROTYPIC_SYNONYM_OF();
             targetTaxon.addSynonym(synonym, relType);
         }
-
     }
 
     @Override
@@ -411,29 +618,26 @@ public class TaxonServiceImpl
         this.dao = dao;
     }
 
-
     @Override
-    public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, String authorship, Rank rank, Integer pageSize,Integer pageNumber) {
-        long numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
+    public <T extends TaxonBase> Pager<T> findTaxaByName(Class<T> clazz, String uninomial,     String infragenericEpithet, String specificEpithet,
+            String infraspecificEpithet, String authorshipCache, Rank rank, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
+        long numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorshipCache, rank);
 
-        List<TaxonBase> results = new ArrayList<>();
+        List<T> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again
-            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorship, rank, pageSize, pageNumber);
+            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorshipCache, rank,
+                    pageSize, pageNumber, propertyPaths);
         }
 
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @Override
-    public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet,      String infraspecificEpithet, String authorship, Rank rank, Integer pageSize,Integer pageNumber) {
-        long numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
-
-        List<TaxonBase> results = new ArrayList<>();
-        if(numberOfResults > 0) { // no point checking again
-            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, authorship, rank, pageSize, pageNumber);
-        }
+    public <T extends TaxonBase> List<T> listTaxaByName(Class<T> clazz, String uninomial, String infragenericEpithet, String specificEpithet,
+            String infraspecificEpithet, String authorshipCache, Rank rank, Integer pageSize,Integer pageNumber, List<String> propertyPaths) {
 
-        return results;
+        return findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infragenericEpithet, authorshipCache, rank,
+                pageSize, pageNumber, propertyPaths).getRecords();
     }
 
     @Override
@@ -486,12 +690,12 @@ public class TaxonServiceImpl
 
     @Override
     public List<TaxonRelationship> listTaxonRelationships(Set<TaxonRelationshipType> types,
-            Integer pageSize, Integer pageStart, List<OrderHint> orderHints, List<String> propertyPaths) {
-        Long numberOfResults = dao.countTaxonRelationships(types);
+            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
 
+        Long numberOfResults = dao.countTaxonRelationships(types);
         List<TaxonRelationship> results = new ArrayList<>();
         if(numberOfResults > 0) {
-            results = dao.getTaxonRelationships(types, pageSize, pageStart, orderHints, propertyPaths);
+            results = dao.getTaxonRelationships(types, pageSize, pageNumber, orderHints, propertyPaths);
         }
         return results;
     }
@@ -565,7 +769,6 @@ public class TaxonServiceImpl
         return relatedTaxa;
     }
 
-
     /**
      * Recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
      *  <code>taxon</code> supplied as parameter.
@@ -646,7 +849,6 @@ public class TaxonServiceImpl
         taxon = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
         HomotypicGroupTaxonComparator comparator = new HomotypicGroupTaxonComparator(taxon);
 
-
         //homotypic
         result.add(taxon.getHomotypicSynonymsByHomotypicGroup(comparator));
 
@@ -657,7 +859,6 @@ public class TaxonServiceImpl
         }
 
         return result;
-
     }
 
     @Override
@@ -684,7 +885,7 @@ public class TaxonServiceImpl
 
         if (config.isDoSynonyms() || config.isDoTaxa() || config.isDoNamesWithoutTaxa() || config.isDoTaxaByCommonNames()){
                return dao.getTaxaByNameForEditor(config.isDoTaxa(), config.isDoSynonyms(), config.isDoNamesWithoutTaxa(),
-                       config.isDoMisappliedNames(), config.isDoTaxaByCommonNames(), config.isIncludeUnpublished(),
+                       config.isDoMisappliedNames(), config.isDoTaxaByCommonNames(), config.isIncludeUnpublished(), config.isDoIncludeAuthors(),
                        config.getTitleSearchStringSqlized(), config.getClassification(), config.getSubtree(),
                        config.getMatchMode(), config.getNamedAreas(), config.getOrder());
         }else{
@@ -695,6 +896,7 @@ public class TaxonServiceImpl
     @Override
     public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {
 
+        @SuppressWarnings("rawtypes")
         List<IdentifiableEntity> results = new ArrayList<>();
         long numberOfResults = 0; // overall number of results (as opposed to number of results per page)
         List<TaxonBase> taxa = null;
@@ -723,7 +925,7 @@ public class TaxonServiceImpl
                     configurator.getMatchMode(), configurator.getNamedAreas(), configurator.isIncludeUnpublished(),
                     configurator.getOrder(), configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
             }
-       }
+        }
 
         if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
 
@@ -776,7 +978,7 @@ public class TaxonServiceImpl
     //    logger.setLevel(Level.TRACE);
 //        Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
 
-        logger.trace("listMedia() - START");
+        if (logger.isTraceEnabled()){logger.trace("listMedia() - START");}
 
         Set<Taxon> taxa = new HashSet<>();
         List<Media> taxonMedia = new ArrayList<>();
@@ -788,14 +990,14 @@ public class TaxonServiceImpl
 
         // --- resolve related taxa
         if (includeRelationships != null && ! includeRelationships.isEmpty()) {
-            logger.trace("listMedia() - resolve related taxa");
+            if (logger.isTraceEnabled()){logger.trace("listMedia() - resolve related taxa");}
             taxa = listRelatedTaxa(taxon, includeRelationships, null, includeUnpublished, null, null, null);
         }
 
         taxa.add((Taxon) dao.load(taxon.getUuid()));
 
         if(includeTaxonDescriptions != null && includeTaxonDescriptions){
-            logger.trace("listMedia() - includeTaxonDescriptions");
+            if (logger.isTraceEnabled()){logger.trace("listMedia() - includeTaxonDescriptions");}
             List<TaxonDescription> taxonDescriptions = new ArrayList<>();
             // --- TaxonDescriptions
             for (Taxon t : taxa) {
@@ -821,7 +1023,8 @@ public class TaxonServiceImpl
 
 
         if(includeOccurrences != null && includeOccurrences) {
-            logger.trace("listMedia() - includeOccurrences");
+            if (logger.isTraceEnabled()){logger.trace("listMedia() - includeOccurrences");}
+            @SuppressWarnings("rawtypes")
             Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<>();
             // --- Specimens
             for (Taxon t : taxa) {
@@ -854,16 +1057,16 @@ public class TaxonServiceImpl
                     }
                 }
                 //media in hierarchy
-                taxonMedia.addAll(occurrenceService.getMediainHierarchy(occurrence, null, null, propertyPath).getRecords());
+                taxonMedia.addAll(occurrenceService.getMediaInHierarchy(occurrence, null, null, propertyPath).getRecords());
             }
         }
 
         if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {
-            logger.trace("listMedia() - includeTaxonNameDescriptions");
+            if (logger.isTraceEnabled()){logger.trace("listMedia() - includeTaxonNameDescriptions");}
             // --- TaxonNameDescription
             Set<TaxonNameDescription> nameDescriptions = new HashSet<>();
             for (Taxon t : taxa) {
-                nameDescriptions .addAll(t.getName().getDescriptions());
+                nameDescriptions.addAll(t.getName().getDescriptions());
             }
             for(TaxonNameDescription nameDescription: nameDescriptions){
                 if (!limitToGalleries || nameDescription.isImageGallery()) {
@@ -877,18 +1080,23 @@ public class TaxonServiceImpl
             }
         }
 
+        taxonMedia = deduplicateMedia(taxonMedia);
 
-        logger.trace("listMedia() - initialize");
+        if (logger.isTraceEnabled()){logger.trace("listMedia() - initialize");}
         beanInitializer.initializeAll(taxonMedia, propertyPath);
 
-        logger.trace("listMedia() - END");
+        if (logger.isTraceEnabled()){logger.trace("listMedia() - END");}
 
         return taxonMedia;
     }
 
+    private List<Media> deduplicateMedia(List<Media> taxonMedia) {
+        return taxonMedia.stream().distinct().collect(Collectors.toList());
+    }
+
     @Override
     public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
-        return this.dao.loadList(listOfIDs, null);
+        return this.dao.loadList(listOfIDs, null, null);
     }
 
     @Override
@@ -901,11 +1109,6 @@ public class TaxonServiceImpl
         return this.dao.countSynonyms(onlyAttachedToTaxon);
     }
 
-    @Override
-    public List<TaxonName> findIdenticalTaxonNames(List<String> propertyPath) {
-        return this.dao.findIdenticalTaxonNames(propertyPath);
-    }
-
     @Override
     @Transactional(readOnly=false)
     public DeleteResult deleteTaxon(UUID taxonUUID, TaxonDeletionConfigurator config, UUID classificationUuid)  {
@@ -922,6 +1125,7 @@ public class TaxonServiceImpl
        }
        taxon = HibernateProxyHelper.deproxy(taxon);
        Classification classification = HibernateProxyHelper.deproxy(classificationDao.load(classificationUuid), Classification.class);
+       config.setClassificationUuid(classificationUuid);
         result = isDeletable(taxonUUID, config);
 
         if (result.isOk()){
@@ -957,8 +1161,8 @@ public class TaxonServiceImpl
                 configRelTaxon.setDeleteConceptRelationships(true);
 
                 for (TaxonRelationship taxRel: taxon.getTaxonRelations()){
-                    if (config.isDeleteMisappliedNamesAndInvalidDesignations()
-                            && taxRel.getType().isMisappliedNameOrInvalidDesignation()
+                    if (config.isDeleteMisappliedNames()
+                            && taxRel.getType().isMisappliedName()
                             && taxon.equals(taxRel.getToTaxon())){
                         this.deleteTaxon(taxRel.getFromTaxon().getUuid(), config, classificationUuid);
                     } else if (config.isDeleteConceptRelationships() && taxRel.getType().isConceptRelationship()){
@@ -987,8 +1191,6 @@ public class TaxonServiceImpl
                         break;
                     }
                     removeDescriptions.add(desc);
-
-
                 }
                 if (result.isOk()){
                     for (TaxonDescription desc: removeDescriptions){
@@ -1000,101 +1202,99 @@ public class TaxonServiceImpl
                 }
             }
 
-
-         if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null && taxon.getTaxonNodes().size() > 1)){
-             result.addException(new Exception( "Taxon can't be deleted as it is used in more than one classification."));
-         }else{
-             if (taxon.getTaxonNodes().size() != 0){
-                Set<TaxonNode> nodes = taxon.getTaxonNodes();
-                Iterator<TaxonNode> iterator = nodes.iterator();
-                TaxonNode node = null;
-                boolean deleteChildren;
-                if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
-                    deleteChildren = true;
-                }else {
-                    deleteChildren = false;
-                }
-                boolean success = true;
-                if (!config.isDeleteInAllClassifications() && !(classification == null)){
-                    while (iterator.hasNext()){
-                        node = iterator.next();
-                        if (node.getClassification().equals(classification)){
-                            break;
-                        }
-                        node = null;
-                    }
-                    if (node != null){
-                        HibernateProxyHelper.deproxy(node, TaxonNode.class);
-                        success =taxon.removeTaxonNode(node, deleteChildren);
-                        nodeService.delete(node);
-                        result.addDeletedObject(node);
-                    } else {
-                       result.setError();
-                       result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
+             if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null && taxon.getTaxonNodes().size() > 1)){
+                 result.addException(new Exception( "Taxon can't be deleted as it is used in more than one classification."));
+             }else{
+                 if (taxon.getTaxonNodes().size() != 0){
+                    Set<TaxonNode> nodes = taxon.getTaxonNodes();
+                    Iterator<TaxonNode> iterator = nodes.iterator();
+                    TaxonNode node = null;
+                    boolean deleteChildren;
+                    if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
+                        deleteChildren = true;
+                    }else {
+                        deleteChildren = false;
                     }
-                } else if (config.isDeleteInAllClassifications()){
-                    List<TaxonNode> nodesList = new ArrayList<>();
-                    nodesList.addAll(taxon.getTaxonNodes());
-                    for (ITaxonTreeNode treeNode: nodesList){
-                        TaxonNode taxonNode = (TaxonNode) treeNode;
-                        if(!deleteChildren){
-                            Object[] childNodes = taxonNode.getChildNodes().toArray();
-                            for (Object childNode: childNodes){
-                                TaxonNode childNodeCast = (TaxonNode) childNode;
-                                taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());
+                    boolean success = true;
+                    if (!config.isDeleteInAllClassifications() && !(classification == null)){
+                        while (iterator.hasNext()){
+                            node = iterator.next();
+                            if (node.getClassification().equals(classification)){
+                                break;
                             }
+                            node = null;
+                        }
+                        if (node != null){
+                            HibernateProxyHelper.deproxy(node, TaxonNode.class);
+                            success =taxon.removeTaxonNode(node, deleteChildren);
+                            nodeService.delete(node);
+                            result.addDeletedObject(node);
+                        } else {
+                               result.setError();
+                               result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
+                        }
+                    } else if (config.isDeleteInAllClassifications()){
+                        List<TaxonNode> nodesList = new ArrayList<>();
+                        nodesList.addAll(taxon.getTaxonNodes());
+                        for (ITaxonTreeNode treeNode: nodesList){
+                            TaxonNode taxonNode = (TaxonNode) treeNode;
+                            if(!deleteChildren){
+                                Object[] childNodes = taxonNode.getChildNodes().toArray();
+                                for (Object childNode: childNodes){
+                                    TaxonNode childNodeCast = (TaxonNode) childNode;
+                                    taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());
+                                }
+                            }
+                        }
+                        config.getTaxonNodeConfig().setDeleteElement(false);
+                        DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);
+                        if (!resultNodes.isOk()){
+                               result.addExceptions(resultNodes.getExceptions());
+                               result.setStatus(resultNodes.getStatus());
+                        } else {
+                            result.addUpdatedObjects(resultNodes.getUpdatedObjects());
                         }
                     }
-                    config.getTaxonNodeConfig().setDeleteElement(false);
-                    DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);
-                    if (!resultNodes.isOk()){
-                       result.addExceptions(resultNodes.getExceptions());
-                       result.setStatus(resultNodes.getStatus());
-                    } else {
-                        result.addUpdatedObjects(resultNodes.getUpdatedObjects());
+                    if (!success){
+                        result.setError();
+                        result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
                     }
                 }
-                if (!success){
-                    result.setError();
-                    result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
-                }
-            }
-         }
-         TaxonName name = taxon.getName();
-         taxon.setName(null);
-         this.saveOrUpdate(taxon);
-
-         if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0)  && result.isOk()){
-             try{
-                 dao.delete(taxon);
-                 result.addDeletedObject(taxon);
-             }catch(Exception e){
-                 result.addException(e);
-                 result.setError();
              }
-         } else {
-             result.setError();
-             result.addException(new Exception("The Taxon can't be deleted because it is used in a classification."));
+             TaxonName name = taxon.getName();
+             taxon.setName(null);
+             this.saveOrUpdate(taxon);
+
+             if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0)  && result.isOk()){
+                 try{
+                     dao.delete(taxon);
+                     result.addDeletedObject(taxon);
+                 }catch(Exception e){
+                     result.addException(e);
+                     result.setError();
+                 }
+             } else {
+                 result.setError();
+                 result.addException(new Exception("The Taxon can't be deleted because it is used in a classification."));
 
-         }
-         //TaxonName
-         if (config.isDeleteNameIfPossible() && result.isOk()){
-            DeleteResult nameResult = new DeleteResult();
-            //remove name if possible (and required)
-            if (name != null ){
-                nameResult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());
-            }
-            if (nameResult.isError() || nameResult.isAbort()){
-                result.addRelatedObject(name);
-                result.addExceptions(nameResult.getExceptions());
-            }else{
-                result.includeResult(nameResult);
-            }
-         }
+             }
+             //TaxonName
+             if (config.isDeleteNameIfPossible() && result.isOk()){
+                 DeleteResult nameResult = new DeleteResult();
+                 //remove name if possible (and required)
+                 if (name != null ){
+                     nameResult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());
+                 }
+                 if (nameResult.isError() || nameResult.isAbort()){
+                     result.addRelatedObject(name);
+                     result.addExceptions(nameResult.getExceptions());
+                 }else{
+                     result.includeResult(nameResult);
+                 }
+             }
        }
 
        return result;
-
     }
 
     @Override
@@ -1108,10 +1308,8 @@ public class TaxonServiceImpl
     @Transactional(readOnly = false)
     public DeleteResult deleteSynonym(UUID synonymUuid, SynonymDeletionConfigurator config) {
         return deleteSynonym((Synonym)dao.load(synonymUuid), config);
-
     }
 
-
     @Override
     @Transactional(readOnly = false)
     public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {
@@ -1154,26 +1352,24 @@ public class TaxonServiceImpl
             //remove name if possible (and required)
             if (name != null && config.isDeleteNameIfPossible()){
 
-                    DeleteResult nameDeleteResult = nameService.delete(name, config.getNameDeletionConfig());
-                    if (nameDeleteResult.isAbort() || nameDeleteResult.isError()){
-                       result.addExceptions(nameDeleteResult.getExceptions());
-                       result.addRelatedObject(name);
-                    }else{
-                        result.addDeletedObject(name);
-                    }
+                DeleteResult nameDeleteResult = nameService.delete(name, config.getNameDeletionConfig());
+                if (nameDeleteResult.isAbort() || nameDeleteResult.isError()){
+                       result.addExceptions(nameDeleteResult.getExceptions());
+                       result.addRelatedObject(name);
+                       result.addUpdatedObject(name);
+                }else{
+                    result.addDeletedObject(name);
+                }
             }
-
         }
         return result;
     }
 
     @Override
-    public List<TaxonName> findIdenticalTaxonNameIds(List<String> propertyPath) {
-
-        return this.dao.findIdenticalNamesNew(propertyPath);
+    public Map<String, Map<UUID,Set<TaxonName>>> findIdenticalTaxonNames(List<UUID> sourceRefUuids, List<String> propertyPaths) {
+        return this.dao.findIdenticalNames(sourceRefUuids, propertyPaths);
     }
 
-
     @Override
     public Taxon findBestMatchingTaxon(String taxonName) {
         MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
@@ -1219,7 +1415,6 @@ public class TaxonServiceImpl
                         countEqualCandidates = 1;
                         continue;
                     }
-
                 }else{  //not Taxon.class
                     continue;
                 }
@@ -1236,7 +1431,6 @@ public class TaxonServiceImpl
                 }
             }
 
-
             // 2. search for synonyms
             if (config.isIncludeSynonyms()){
                 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.isIncludeUnpublished(),
@@ -1301,6 +1495,20 @@ public class TaxonServiceImpl
         return null;
     }
 
+    @Override
+    @Transactional(readOnly = false)
+    public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym, UUID newTaxonUUID, boolean moveHomotypicGroup,
+            SynonymType newSynonymType, UUID newSecundumUuid, String newSecundumDetail,
+            boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
+
+        UpdateResult result = new UpdateResult();
+        Taxon newTaxon = CdmBase.deproxy(dao.load(newTaxonUUID), Taxon.class);
+        result = moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup, newSynonymType,
+                newSecundumUuid, newSecundumDetail, keepSecundumIfUndefined);
+
+        return result;
+    }
+
     @Override
     @Transactional(readOnly = false)
     public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym,
@@ -1309,7 +1517,7 @@ public class TaxonServiceImpl
             SynonymType newSynonymType) throws HomotypicalGroupChangeException {
         return moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup,
                 newSynonymType,
-                oldSynonym.getSec(),
+                oldSynonym.getSec()!= null? oldSynonym.getSec().getUuid(): null,
                 oldSynonym.getSecMicroReference(),
                 true);
     }
@@ -1320,7 +1528,7 @@ public class TaxonServiceImpl
             Taxon newTaxon,
             boolean moveHomotypicGroup,
             SynonymType newSynonymType,
-            Reference newSecundum,
+            UUID newSecundumUuid,
             String newSecundumDetail,
             boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
 
@@ -1360,6 +1568,7 @@ public class TaxonServiceImpl
         UpdateResult result = new UpdateResult();
         //move all synonyms to new taxon
         List<Synonym> homotypicSynonyms = oldTaxon.getSynonymsInGroup(homotypicGroup);
+        Reference newSecundum = referenceService.load(newSecundumUuid);
         for (Synonym synRelation: homotypicSynonyms){
 
             newTaxon = HibernateProxyHelper.deproxy(newTaxon, Taxon.class);
@@ -1382,6 +1591,8 @@ public class TaxonServiceImpl
 
         result.addUpdatedObject(oldTaxon);
         result.addUpdatedObject(newTaxon);
+        result.addUpdatedObject(homotypicGroup);
+        result.addUpdatedObject(synonym);
         saveOrUpdate(oldTaxon);
         saveOrUpdate(newTaxon);
 
@@ -1389,7 +1600,8 @@ public class TaxonServiceImpl
     }
 
     @Override
-    public <T extends TaxonBase> List<UuidAndTitleCache<T>> getUuidAndTitleCache(Class<T> clazz, Integer limit, String pattern) {
+    public <T extends TaxonBase>List<UuidAndTitleCache<T>> getUuidAndTitleCache(Class<T> clazz, Integer limit, String pattern) {
+
         return dao.getUuidAndTitleCache(clazz, limit, pattern);
     }
 
@@ -1414,10 +1626,11 @@ public class TaxonServiceImpl
         }
 
         Map<CdmBaseType, String> idFieldMap = new HashMap<>();
-        idFieldMap.put(CdmBaseType.TAXON, "id");
+        idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
 
         // ---  initialize taxa, thighlight matches ....
         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+        @SuppressWarnings("rawtypes")
         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
                 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
 
@@ -1425,6 +1638,43 @@ public class TaxonServiceImpl
         return new DefaultPagerImpl<>(pageNumber, totalHits, pageSize, searchResults);
     }
 
+    @Transactional(readOnly = true)
+    @Override
+    public <S extends TaxonBase> Pager<S> findByTitleWithRestrictions(Class<S> clazz, String queryString, MatchMode matchmode, List<Restriction<?>> restrictions, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         long numberOfResults = dao.countByTitleWithRestrictions(clazz, queryString, matchmode, restrictions);
+
+         long numberOfResults_doubtful = dao.countByTitleWithRestrictions(clazz, "?".concat(queryString), matchmode, restrictions);
+         List<S> results = new ArrayList<>();
+         if(numberOfResults > 0 || numberOfResults_doubtful > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
+
+             results = dao.findByTitleWithRestrictions(clazz, queryString, matchmode, restrictions, pageSize, pageNumber, orderHints, propertyPaths);
+             results.addAll(dao.findByTitleWithRestrictions(clazz, "?".concat(queryString), matchmode, restrictions, pageSize, pageNumber, orderHints, propertyPaths));
+         }
+         Collections.sort(results, new TaxonComparator());
+         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public <S extends TaxonBase> Pager<S> findByTitle(Class<S> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+        long numberOfResults = dao.countByTitle(clazz, queryString, matchmode, criteria);
+        //check whether there are doubtful taxa matching
+        long numberOfResults_doubtful = dao.countByTitle(clazz, "?".concat(queryString), matchmode, criteria);
+        List<S> results = new ArrayList<>();
+        if(numberOfResults > 0 || numberOfResults_doubtful > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
+               if (numberOfResults > 0){
+                   results = dao.findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
+               }else{
+                   results = new ArrayList<>();
+               }
+               if (numberOfResults_doubtful > 0){
+                   results.addAll(dao.findByTitle(clazz, "?".concat(queryString), matchmode,  criteria, pageSize, pageNumber, orderHints, propertyPaths));
+               }
+        }
+        Collections.sort(results, new TaxonComparator());
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
+    }
+
     @Override
     public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTerm> statusFilter,
             Classification classification, TaxonNode subtree,
@@ -1444,7 +1694,7 @@ public class TaxonServiceImpl
         }
 
         Map<CdmBaseType, String> idFieldMap = new HashMap<>();
-        idFieldMap.put(CdmBaseType.TAXON, "id");
+        idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
 
         // ---  initialize taxa, thighlight matches ....
         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
@@ -1521,7 +1771,7 @@ public class TaxonServiceImpl
      * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
      * the BlockJoinQuery could be used. The latter might be more memory save but has the
      * drawback of requiring to do the join an indexing time.
-     * see  http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
+     * see  https://dev.e-taxonomy.eu/redmine/projects/edit/wiki/LuceneNotes#JoinsinLucene for more information on this.
      *
      * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
      * <ul>
@@ -1610,11 +1860,12 @@ public class TaxonServiceImpl
             Set<NamedArea> namedAreas, Set<PresenceAbsenceTerm> distributionStatus, List<Language> languages,
             boolean highlightFragments, Integer pageSize,
             Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)
-            throws IOException, LuceneParseException, LuceneMultiSearchException {
+                    throws IOException, LuceneParseException, LuceneMultiSearchException {
 
         // FIXME: allow taxonomic ordering
-        //  hql equivalent:  order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
-        // this require building a special sort column by a special classBridge
+        //  hql equivalent:  order by t.name.genusOrUninomial, case when t.name.specificEpithet
+        // like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
+        // this requires building a special sort column by a special classBridge
         if(highlightFragments){
             logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
                     "currently not fully supported by this method and thus " +
@@ -1651,7 +1902,6 @@ public class TaxonServiceImpl
 //        SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
 //        SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
 
-
         boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;
 
         List<LuceneSearch> luceneSearches = new ArrayList<>();
@@ -1714,7 +1964,7 @@ public class TaxonServiceImpl
             luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass,
                     queryString, classification, subtree, className,
                     includeUnpublished, languages, highlightFragments, sortFields));
-            idFieldMap.put(CdmBaseType.TAXON, "id");
+            idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
             /* A) does not work!!!!
             if(addDistributionFilter){
                 // in this case we need a filter which uses a join query
@@ -1766,7 +2016,7 @@ public class TaxonServiceImpl
             byCommonNameSearch.setQuery(builder.build());
             byCommonNameSearch.setSortFields(sortFields);
 
-            idFieldMap.put(CdmBaseType.TAXON, "id");
+            idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
 
             luceneSearches.add(byCommonNameSearch);
 
@@ -1809,7 +2059,7 @@ public class TaxonServiceImpl
             luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
                     new TaxonRelationshipEdge(relTypes, Direction.relatedTo),
                     queryString, classification, subtree, includeUnpublished, languages, highlightFragments, sortFields));
-            idFieldMap.put(CdmBaseType.TAXON, "id");
+            idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
 
             if(addDistributionFilter){
                 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
@@ -1855,7 +2105,6 @@ public class TaxonServiceImpl
             }
         }
 
-
         // search by pro parte synonyms
         if(searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
             //TODO merge with misapplied name search once #7487 is fixed
@@ -1865,7 +2114,7 @@ public class TaxonServiceImpl
             luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
                     new TaxonRelationshipEdge(relTypes, Direction.relatedTo),
                     queryString, classification, subtree, includeUnpublished, languages, highlightFragments, sortFields));
-            idFieldMap.put(CdmBaseType.TAXON, "id");
+            idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
 
             if(addDistributionFilter){
                 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
@@ -1877,12 +2126,9 @@ public class TaxonServiceImpl
             }
         }//end pro parte synonyms
 
-
-
         LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,
                 luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));
 
-
         if(addDistributionFilter){
 
             // B)
@@ -2024,7 +2270,6 @@ public class TaxonServiceImpl
             Classification classification, TaxonNode subtree, List<Feature> features, List<Language> languages,
             boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, LuceneParseException {
 
-
         LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, subtree, features, languages, highlightFragments);
 
         // --- execute search
@@ -2048,10 +2293,8 @@ public class TaxonServiceImpl
 
         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
         return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
-
     }
 
-
     @Override
     public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,
             Classification classification, TaxonNode subtree, boolean includeUnpublished, List<Language> languages, boolean highlightFragments,
@@ -2079,7 +2322,7 @@ public class TaxonServiceImpl
         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
 
         Map<CdmBaseType, String> idFieldMap = new HashMap<>();
-        idFieldMap.put(CdmBaseType.TAXON, "id");
+        idFieldMap.put(CdmBaseType.TAXON_BASE, "id");
         idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
 
         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
@@ -2087,10 +2330,8 @@ public class TaxonServiceImpl
 
         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
         return new DefaultPagerImpl<>(pageNumber, Long.valueOf(totalHits), pageSize, searchResults);
-
     }
 
-
     /**
      * @param clazz
      * @param queryString
@@ -2202,7 +2443,6 @@ public class TaxonServiceImpl
     @Override
     public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymType type, boolean doWithMisappliedNames){
 
-
         List <Synonym> inferredSynonyms = new ArrayList<>();
         List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<>();
 
@@ -2299,140 +2539,87 @@ public class TaxonServiceImpl
                             }
 
                             if (!taxonNames.isEmpty()){
-                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
-                            IZoologicalName name;
-                            if (!synNotInCDM.isEmpty()){
-                                inferredSynonymsToBeRemoved.clear();
-
-                                for (Synonym syn :inferredSynonyms){
-                                    name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
-                                    if (!synNotInCDM.contains(name.getNameCache())){
-                                        inferredSynonymsToBeRemoved.add(syn);
+                                List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                                if (!synNotInCDM.isEmpty()){
+                                    inferredSynonymsToBeRemoved.clear();
+
+                                    for (Synonym syn :inferredSynonyms){
+                                        IZoologicalName name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                        if (!synNotInCDM.contains(name.getNameCache())){
+                                            inferredSynonymsToBeRemoved.add(syn);
+                                        }
                                     }
-                                }
 
-                                // Remove identified Synonyms from inferredSynonyms
-                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
-                                    inferredSynonyms.remove(synonym);
+                                    // Remove identified Synonyms from inferredSynonyms
+                                    for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                        inferredSynonyms.remove(synonym);
+                                    }
                                 }
                             }
-                        }
-
-                    }else if (type.equals(SynonymType.INFERRED_GENUS_OF())){
 
-                        for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
+                        }else if (type.equals(SynonymType.INFERRED_GENUS_OF())){
 
-                            inferredGenus = createInferredGenus(taxon,
-                                    zooHashMap, taxonName, epithetOfTaxon,
-                                    genusOfTaxon, taxonNames, zooParentName, synonymRelationOfTaxon);
-
-                            inferredSynonyms.add(inferredGenus);
-                            zooHashMap.put(inferredGenus.getName().getUuid(), inferredGenus.getName());
-                            taxonNames.add(inferredGenus.getName().getNameCache());
-                        }
-
-                        if (doWithMisappliedNames){
+                            for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
 
-                            for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
-                                Taxon misappliedName = taxonRelationship.getFromTaxon();
-                                inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);
+                                inferredGenus = createInferredGenus(taxon,
+                                        zooHashMap, taxonName, epithetOfTaxon,
+                                        genusOfTaxon, taxonNames, zooParentName, synonymRelationOfTaxon);
 
                                 inferredSynonyms.add(inferredGenus);
                                 zooHashMap.put(inferredGenus.getName().getUuid(), inferredGenus.getName());
-                                 taxonNames.add(inferredGenus.getName().getNameCache());
+                                taxonNames.add(inferredGenus.getName().getNameCache());
                             }
-                        }
-
 
-                        if (!taxonNames.isEmpty()){
-                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
-                            IZoologicalName name;
-                            if (!synNotInCDM.isEmpty()){
-                                inferredSynonymsToBeRemoved.clear();
+                            if (doWithMisappliedNames){
 
-                                for (Synonym syn :inferredSynonyms){
-                                    name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
-                                    if (!synNotInCDM.contains(name.getNameCache())){
-                                        inferredSynonymsToBeRemoved.add(syn);
-                                    }
-                                }
+                                for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
+                                    Taxon misappliedName = taxonRelationship.getFromTaxon();
+                                    inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);
 
-                                // Remove identified Synonyms from inferredSynonyms
-                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
-                                    inferredSynonyms.remove(synonym);
+                                    inferredSynonyms.add(inferredGenus);
+                                    zooHashMap.put(inferredGenus.getName().getUuid(), inferredGenus.getName());
+                                     taxonNames.add(inferredGenus.getName().getNameCache());
                                 }
                             }
-                        }
 
-                    }else if (type.equals(SynonymType.POTENTIAL_COMBINATION_OF())){
 
-                        Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
-                        //for all synonyms of the parent...
-                        for (Synonym synonymRelationOfParent:synonyMsOfParent){
-                            TaxonName synName;
-                            HibernateProxyHelper.deproxy(synonymRelationOfParent);
-
-                            synName = synonymRelationOfParent.getName();
-
-                            // Set the sourceReference
-                            sourceReference = synonymRelationOfParent.getSec();
-
-                            // Determine the idInSource
-                            String idInSourceParent = getIdInSource(synonymRelationOfParent);
-
-                            IZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
-                            String synParentGenus = parentSynZooName.getGenusOrUninomial();
-                            String synParentInfragenericName = null;
-                            String synParentSpecificEpithet = null;
-
-                            if (parentSynZooName.isInfraGeneric()){
-                                synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
-                            }
-                            if (parentSynZooName.isSpecies()){
-                                synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
-                            }
-
-                           /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
-                                synonymsGenus.put(synGenusName, idInSource);
-                            }*/
-
-                            //for all synonyms of the taxon
-
-                            for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
-
-                                IZoologicalName zooSynName = getZoologicalName(synonymRelationOfTaxon.getName().getUuid(), zooHashMap);
-                                potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
-                                        synParentGenus,
-                                        synParentInfragenericName,
-                                        synParentSpecificEpithet, synonymRelationOfTaxon, zooHashMap);
+                            if (!taxonNames.isEmpty()){
+                                List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                                IZoologicalName name;
+                                if (!synNotInCDM.isEmpty()){
+                                    inferredSynonymsToBeRemoved.clear();
 
-                                taxon.addSynonym(potentialCombination, SynonymType.POTENTIAL_COMBINATION_OF());
-                                inferredSynonyms.add(potentialCombination);
-                                zooHashMap.put(potentialCombination.getName().getUuid(), potentialCombination.getName());
-                                 taxonNames.add(potentialCombination.getName().getNameCache());
+                                    for (Synonym syn :inferredSynonyms){
+                                        name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                        if (!synNotInCDM.contains(name.getNameCache())){
+                                            inferredSynonymsToBeRemoved.add(syn);
+                                        }
+                                    }
 
+                                    // Remove identified Synonyms from inferredSynonyms
+                                    for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                        inferredSynonyms.remove(synonym);
+                                    }
+                                }
                             }
 
-                        }
-
-                        if (doWithMisappliedNames){
-
-                            for (TaxonRelationship parentRelationship: taxonRelListParent){
+                        }else if (type.equals(SynonymType.POTENTIAL_COMBINATION_OF())){
 
-                                TaxonName misappliedParentName;
-
-                                Taxon misappliedParent = parentRelationship.getFromTaxon();
-                                misappliedParentName = misappliedParent.getName();
+                            Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
+                            //for all synonyms of the parent...
+                            for (Synonym synonymRelationOfParent:synonyMsOfParent){
+                                TaxonName synName;
+                                HibernateProxyHelper.deproxy(synonymRelationOfParent);
 
-                                HibernateProxyHelper.deproxy(misappliedParent);
+                                synName = synonymRelationOfParent.getName();
 
                                 // Set the sourceReference
-                                sourceReference = misappliedParent.getSec();
+                                sourceReference = synonymRelationOfParent.getSec();
 
                                 // Determine the idInSource
-                                String idInSourceParent = getIdInSource(misappliedParent);
+                                String idInSourceParent = getIdInSource(synonymRelationOfParent);
 
-                                IZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
+                                IZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
                                 String synParentGenus = parentSynZooName.getGenusOrUninomial();
                                 String synParentInfragenericName = null;
                                 String synParentSpecificEpithet = null;
@@ -2444,16 +2631,19 @@ public class TaxonServiceImpl
                                     synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
                                 }
 
+                               /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
+                                    synonymsGenus.put(synGenusName, idInSource);
+                                }*/
 
-                                for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
-                                    Taxon misappliedName = taxonRelationship.getFromTaxon();
-                                    IZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
-                                    potentialCombination = createPotentialCombination(
-                                            idInSourceParent, parentSynZooName, zooMisappliedName,
+                                //for all synonyms of the taxon
+
+                                for (Synonym synonymRelationOfTaxon:synonymsOfTaxon){
+
+                                    IZoologicalName zooSynName = getZoologicalName(synonymRelationOfTaxon.getName().getUuid(), zooHashMap);
+                                    potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
                                             synParentGenus,
                                             synParentInfragenericName,
-                                            synParentSpecificEpithet, misappliedName, zooHashMap);
-
+                                            synParentSpecificEpithet, synonymRelationOfTaxon, zooHashMap);
 
                                     taxon.addSynonym(potentialCombination, SynonymType.POTENTIAL_COMBINATION_OF());
                                     inferredSynonyms.add(potentialCombination);
@@ -2461,29 +2651,74 @@ public class TaxonServiceImpl
                                      taxonNames.add(potentialCombination.getName().getNameCache());
                                 }
                             }
-                        }
 
-                        if (!taxonNames.isEmpty()){
-                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
-                            IZoologicalName name;
-                            if (!synNotInCDM.isEmpty()){
-                                inferredSynonymsToBeRemoved.clear();
-                                for (Synonym syn :inferredSynonyms){
-                                    try{
-                                        name = syn.getName();
-                                    }catch (ClassCastException e){
-                                        name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                            if (doWithMisappliedNames){
+
+                                for (TaxonRelationship parentRelationship: taxonRelListParent){
+
+                                    TaxonName misappliedParentName;
+
+                                    Taxon misappliedParent = parentRelationship.getFromTaxon();
+                                    misappliedParentName = misappliedParent.getName();
+
+                                    HibernateProxyHelper.deproxy(misappliedParent);
+
+                                    // Set the sourceReference
+                                    sourceReference = misappliedParent.getSec();
+
+                                    // Determine the idInSource
+                                    String idInSourceParent = getIdInSource(misappliedParent);
+
+                                    IZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
+                                    String synParentGenus = parentSynZooName.getGenusOrUninomial();
+                                    String synParentInfragenericName = null;
+                                    String synParentSpecificEpithet = null;
+
+                                    if (parentSynZooName.isInfraGeneric()){
+                                        synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
+                                    }
+                                    if (parentSynZooName.isSpecies()){
+                                        synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
+                                    }
+
+                                    for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
+                                        Taxon misappliedName = taxonRelationship.getFromTaxon();
+                                        IZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
+                                        potentialCombination = createPotentialCombination(
+                                                idInSourceParent, parentSynZooName, zooMisappliedName,
+                                                synParentGenus,
+                                                synParentInfragenericName,
+                                                synParentSpecificEpithet, misappliedName, zooHashMap);
+
+                                        taxon.addSynonym(potentialCombination, SynonymType.POTENTIAL_COMBINATION_OF());
+                                        inferredSynonyms.add(potentialCombination);
+                                        zooHashMap.put(potentialCombination.getName().getUuid(), potentialCombination.getName());
+                                         taxonNames.add(potentialCombination.getName().getNameCache());
                                     }
-                                    if (!synNotInCDM.contains(name.getNameCache())){
-                                        inferredSynonymsToBeRemoved.add(syn);
+                                }
+                            }
+
+                            if (!taxonNames.isEmpty()){
+                                List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                                IZoologicalName name;
+                                if (!synNotInCDM.isEmpty()){
+                                    inferredSynonymsToBeRemoved.clear();
+                                    for (Synonym syn :inferredSynonyms){
+                                        try{
+                                            name = syn.getName();
+                                        }catch (ClassCastException e){
+                                            name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                        }
+                                        if (!synNotInCDM.contains(name.getNameCache())){
+                                            inferredSynonymsToBeRemoved.add(syn);
+                                        }
+                                     }
+                                    // Remove identified Synonyms from inferredSynonyms
+                                    for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                        inferredSynonyms.remove(synonym);
                                     }
-                                 }
-                                // Remove identified Synonyms from inferredSynonyms
-                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
-                                    inferredSynonyms.remove(synonym);
                                 }
                             }
-                         }
                         }
                     }else {
                         logger.info("The synonym type is not defined.");
@@ -2491,7 +2726,6 @@ public class TaxonServiceImpl
                     }
                 }
             }
-
         }
 
         return inferredSynonyms;
@@ -2547,7 +2781,6 @@ public class TaxonServiceImpl
             inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
         }
 
-
         potentialCombination = Synonym.NewInstance(inferredSynName, null);
 
         // Set the sourceReference
@@ -2590,14 +2823,13 @@ public class TaxonServiceImpl
         synName = syn.getName();
         IZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
         String synSpeciesEpithetName = synZooName.getSpecificEpithet();
-                     /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
+         /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
             synonymsEpithet.add(synSpeciesEpithetName);
         }*/
 
         inferredSynName = TaxonNameFactory.NewZoologicalInstance(taxon.getName().getRank());
         //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
 
-
         inferredSynName.setGenusOrUninomial(genusOfTaxon);
         if (zooParentName.isInfraGeneric()){
             inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
@@ -2611,7 +2843,6 @@ public class TaxonServiceImpl
             inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
         }
 
-
         inferredGenus = Synonym.NewInstance(inferredSynName, null);
 
         // Set the sourceReference
@@ -2681,7 +2912,7 @@ public class TaxonServiceImpl
             synSpecificEpithet = zooSynName.getSpecificEpithet();
         }
 
-                     /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
+           /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
             synonymsGenus.put(synGenusName, idInSource);
         }*/
 
@@ -2732,8 +2963,6 @@ public class TaxonServiceImpl
 
         inferredSynName.addSource(originalSource);
 
-
-
         taxon.addSynonym(inferredEpithet, SynonymType.INFERRED_EPITHET_OF());
 
         return inferredEpithet;
@@ -2780,11 +3009,9 @@ public class TaxonServiceImpl
             logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
         }
 
-
         return idInSource;
     }
 
-
     /**
      * Returns the citation for a given Synonym.
      * @param syn
@@ -2855,7 +3082,6 @@ public class TaxonServiceImpl
         Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
         result = changeRelatedTaxonToSynonym(fromTaxon, toTaxon, oldRelationshipType, synonymType);
 
-        result.addUpdatedObject(fromTaxon);
         result.addUpdatedObject(toTaxon);
         result.addUpdatedObject(result.getCdmEntity());
 
@@ -2883,7 +3109,8 @@ public class TaxonServiceImpl
         } else{
             synonym = toTaxon.addHeterotypicSynonymName(synonymName);
         }
-
+        //keep the publish flag
+        synonym.setPublish(fromTaxon.isPublish());
         this.saveOrUpdate(toTaxon);
         //TODO: configurator and classification
         TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();
@@ -2902,20 +3129,42 @@ public class TaxonServiceImpl
         Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);
         if (taxonBase instanceof Taxon){
             TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;
-            result = isDeletableForTaxon(references, taxonConfig);
+            List<String> propertyPaths = new ArrayList<>();
+            propertyPaths.add("taxonNodes");
+            Taxon taxon = (Taxon)load(taxonBaseUuid, propertyPaths);
+
+            result = isDeletableForTaxon(references, taxonConfig );
+
+            if (taxonConfig.isDeleteNameIfPossible()){
+                if (taxonBase.getName() != null){
+                    DeleteResult nameResult = nameService.isDeletable(taxonBase.getName().getUuid(), taxonConfig.getNameDeletionConfig(), taxon.getUuid());
+                    if (!nameResult.isOk()){
+                        result.addExceptions(nameResult.getExceptions());
+                    }
+                }
+
+            }
         }else{
             SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;
             result = isDeletableForSynonym(references, synonymConfig);
+            if (synonymConfig.isDeleteNameIfPossible() && taxonBase.getName() != null){
+                DeleteResult nameResult = nameService.isDeletable(taxonBase.getName().getUuid(), synonymConfig.getNameDeletionConfig(), taxonBase.getUuid());
+                if (!nameResult.isOk()){
+                    result.addExceptions(nameResult.getExceptions());
+
+                }
+            }
         }
+
         return result;
     }
 
     private DeleteResult isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){
-        String message;
+
         DeleteResult result = new DeleteResult();
         for (CdmBase ref: references){
-            if (!(ref instanceof Taxon || ref instanceof TaxonName )){
-                message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();
+            if (!(ref instanceof Taxon || ref instanceof TaxonName || ref instanceof SecundumSource)){
+                String message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();
                 result.addException(new ReferencedObjectUndeletableException(message));
                 result.addRelatedObject(ref);
                 result.setAbort();
@@ -2929,7 +3178,7 @@ public class TaxonServiceImpl
         String message = null;
         DeleteResult result = new DeleteResult();
         for (CdmBase ref: references){
-            if (!(ref instanceof TaxonName)){
+            if (!(ref instanceof TaxonName || ref instanceof SecundumSource)){
                message = null;
                 if (!config.isDeleteSynonymRelations() && (ref instanceof Synonym)){
                     message = "The taxon can't be deleted as long as it has synonyms.";
@@ -2941,9 +3190,13 @@ public class TaxonServiceImpl
                 if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){
                     message = "The taxon can't be deleted as long as it belongs to a taxon node.";
                 }
+                if (ref instanceof TaxonNode && config.getClassificationUuid() != null && !config.isDeleteInAllClassifications() && !((TaxonNode)ref).getClassification().getUuid().equals(config.getClassificationUuid())){
+                    message = "The taxon can't be deleted as long as it is used in more than one classification";
+
+                }
                 if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonRelationship)){
-                    if (!config.isDeleteMisappliedNamesAndInvalidDesignations() &&
-                            (((TaxonRelationship)ref).getType().isMisappliedNameOrInvalidDesignation())){
+                    if (!config.isDeleteMisappliedNames() &&
+                            (((TaxonRelationship)ref).getType().isMisappliedName())){
                         message = "The taxon can't be deleted as long as it has misapplied names or invalid designations.";
                     } else{
                         message = "The taxon can't be deleted as long as it belongs to taxon relationship.";
@@ -2957,7 +3210,6 @@ public class TaxonServiceImpl
                    message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this taxon";
                 }
 
-
                /* //PolytomousKeyNode
                 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
                     String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
@@ -3116,6 +3368,7 @@ public class TaxonServiceImpl
 
     @Override
     public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){
+        @SuppressWarnings("rawtypes")
         List<TaxonBase> taxonList = dao.getTaxaByName(true, config.isIncludeSynonyms(), false, false, false,
                 config.getTaxonNameTitle(), null, null, MatchMode.EXACT, null, config.isIncludeSynonyms(), null, 0, 0, config.getPropertyPath());
         return taxonList;
@@ -3178,20 +3431,6 @@ public class TaxonServiceImpl
     }
 
     @Override
-       @Transactional(readOnly = false)
-       public UpdateResult moveSynonymToAnotherTaxon(Synonym oldSynonym, UUID newTaxonUUID, boolean moveHomotypicGroup,
-            SynonymType newSynonymType, Reference newSecundum, String newSecundumDetail,
-            boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
-
-           UpdateResult result = new UpdateResult();
-               Taxon newTaxon = CdmBase.deproxy(dao.load(newTaxonUUID),Taxon.class);
-               result = moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup, newSynonymType,
-                       newSecundum, newSecundumDetail, keepSecundumIfUndefined);
-
-               return result;
-       }
-
-       @Override
        public UpdateResult moveFactualDateToAnotherTaxon(UUID fromTaxonUuid, UUID toTaxonUuid){
                UpdateResult result = new UpdateResult();
 
@@ -3226,18 +3465,18 @@ public class TaxonServiceImpl
        @Override
        @Transactional(readOnly = false)
        public UpdateResult swapSynonymAndAcceptedTaxon(UUID synonymUUid,
-                       UUID acceptedTaxonUuid) {
+                       UUID acceptedTaxonUuid, boolean setNameInSource, boolean newUuidForAcceptedTaxon, SecReferenceHandlingSwapEnum secHandling, UUID newSecAcc, UUID newSecSyn) {
                TaxonBase<?> base = this.load(synonymUUid);
                Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);
                base = this.load(acceptedTaxonUuid);
                Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);
 
-               return this.swapSynonymAndAcceptedTaxon(syn, taxon);
+               Reference refAcc = referenceService.load(newSecAcc);
+               Reference refSyn = referenceService.load(newSecSyn);
+
+               return this.swapSynonymAndAcceptedTaxon(syn, taxon, setNameInSource, newUuidForAcceptedTaxon, secHandling, refAcc, refSyn);
        }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     public TaxonRelationshipsDTO listTaxonRelationships(UUID taxonUuid, Set<TaxonRelationshipType> directTypes,
             Set<TaxonRelationshipType> inversTypes,
@@ -3290,5 +3529,4 @@ public class TaxonServiceImpl
             return dto;
         }
     }
-
 }