ref #9855 replace deduplication by in CdmUtils by stream.distinct()
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index 47e2d1db67cbae41f8de905aa5453fbda559242d..3abae09387f0975ad535dea7512e6740666cc5f7 100644 (file)
@@ -20,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;
 
@@ -65,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;
@@ -94,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;
@@ -106,13 +113,12 @@ 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;
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
-import eu.etaxonomy.cdm.model.taxon.TaxonComparator;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
@@ -124,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;
@@ -167,6 +174,9 @@ public class TaxonServiceImpl
 
     @Autowired
     private IDescriptionService descriptionService;
+
+    @Autowired
+    private IReferenceService referenceService;
 //
 //    @Autowired
 //    private IOrderedTermVocabularyDao orderedVocabularyDao;
@@ -202,16 +212,83 @@ public class TaxonServiceImpl
 
     @Override
     @Transactional(readOnly = false)
-    public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource){
+    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();
-       acceptedTaxon.removeSynonym(synonym);
+        String oldTaxonTitleCache = acceptedTaxon.getTitleCache();
+
        TaxonName synonymName = synonym.getName();
-       boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(acceptedTaxon.getName().getHomotypicalGroup());
+       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;
+//        }
 
-        synonymName.removeTaxonBase(synonym);
 
-        TaxonName taxonName = HibernateProxyHelper.deproxy(acceptedTaxon.getName(), TaxonName.class);
-        //taxonName.removeTaxonBase(acceptedTaxon);
+       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 synonym, Taxon acceptedTaxon, boolean setNameInSource, SecReferenceHandlingSwapEnum secHandling, Reference newSecAcc, Reference newSecSyn){
+        UpdateResult result = new UpdateResult();
+        acceptedTaxon.removeSynonym(synonym);
+        TaxonName synonymName = synonym.getName();
+        TaxonName taxonName = HibernateProxyHelper.deproxy(acceptedTaxon.getName());
+        String oldTaxonTitleCache = acceptedTaxon.getTitleCache();
+
+        boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(taxonName.getHomotypicalGroup());
+        synonymName.removeTaxonBase(synonym);
 
         List<Synonym> synonyms = new ArrayList<>();
         for (Synonym syn: acceptedTaxon.getSynonyms()){
@@ -221,9 +298,20 @@ public class TaxonServiceImpl
         for (Synonym syn: synonyms){
             acceptedTaxon.removeSynonym(syn);
         }
-        Taxon newTaxon = acceptedTaxon.clone();
+        Taxon newTaxon = acceptedTaxon.clone(true, true, false, true);
+        newTaxon.setSec(newSecAcc);
+
+        //move descriptions
+        Set<TaxonDescription> descriptionsToCopy = new HashSet<>(acceptedTaxon.getDescriptions());
+        for (TaxonDescription description: descriptionsToCopy){
+            newTaxon.addDescription(description);
+        }
+        //nameUsedInSource
+        handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, newTaxon.getDescriptions());
+
         newTaxon.setName(synonymName);
-        newTaxon.setSec(synonym.getSec());
+
+        newTaxon.setPublish(synonym.isPublish());
         for (Synonym syn: synonyms){
             if (!syn.getName().equals(newTaxon.getName())){
                 newTaxon.addSynonym(syn, syn.getType());
@@ -251,54 +339,38 @@ public class TaxonServiceImpl
             }else{
                 logger.warn("Taxon is not part of its own Taxonrelationship");
             }
-            // Remove old relationships
 
+            // Remove old relationships
             fromTaxon.removeTaxonRelation(taxonRelationship);
             toTaxon.removeTaxonRelation(taxonRelationship);
             taxonRelationship.setToTaxon(null);
             taxonRelationship.setFromTaxon(null);
         }
 
-        //Move descriptions to new taxon
-        List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( newTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
-        for(TaxonDescription description : descriptions){
-            String message = "Description copied from former accepted taxon: %s (Old title: %s)";
-            message = String.format(message, acceptedTaxon.getTitleCache(), 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);
-                        }
-                    }
-                }
-            }
-//            //oldTaxon.removeDescription(description, false);
- //           newTaxon.addDescription(description);
-        }
+        //taxon nodes
         List<TaxonNode> nodes = new ArrayList<>(acceptedTaxon.getTaxonNodes());
         for (TaxonNode node: nodes){
-            node = HibernateProxyHelper.deproxy(node, TaxonNode.class);
+            node = HibernateProxyHelper.deproxy(node);
             TaxonNode parent = node.getParent();
             acceptedTaxon.removeTaxonNode(node);
             node.setTaxon(newTaxon);
             if (parent != null){
                 parent.addChildNode(node, null, null);
             }
-
         }
+
+        //synonym
         Synonym newSynonym = synonym.clone();
         newSynonym.setName(taxonName);
-        newSynonym.setSec(acceptedTaxon.getSec());
+        newSynonym.setPublish(acceptedTaxon.isPublish());
+        newSynonym.setSec(newSecSyn);
         if (sameHomotypicGroup){
             newTaxon.addSynonym(newSynonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
         }else{
             newTaxon.addSynonym(newSynonym, SynonymType.HETEROTYPIC_SYNONYM_OF());
         }
 
-        saveOrUpdate(newSynonym);
-        saveOrUpdate(newTaxon);
+        //deletes
         TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
         conf.setDeleteNameIfPossible(false);
         SynonymDeletionConfigurator confSyn = new SynonymDeletionConfigurator();
@@ -307,15 +379,35 @@ public class TaxonServiceImpl
 
         DeleteResult deleteResult = deleteTaxon(acceptedTaxon.getUuid(), conf, null);
         if (synonym.isPersited()){
-            deleteResult.includeResult(deleteSynonym(synonym, confSyn));
+            synonym.setSecSource(null);
+            deleteResult.includeResult(deleteSynonym(synonym.getUuid(), confSyn));
         }
         result.includeResult(deleteResult);
-               return result;
+
+        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();
@@ -329,13 +421,17 @@ public class TaxonServiceImpl
             return result;
         }
 
-        Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, null);
+        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{
@@ -366,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);
@@ -417,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());
 
@@ -760,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{
@@ -853,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<>();
@@ -865,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) {
@@ -898,7 +1023,7 @@ 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
@@ -932,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()) {
@@ -955,14 +1080,20 @@ public class TaxonServiceImpl
             }
         }
 
-        logger.trace("listMedia() - initialize");
+        taxonMedia = deduplicateMedia(taxonMedia);
+
+        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, null);
@@ -1221,13 +1352,14 @@ 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;
@@ -1363,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,
@@ -1371,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);
     }
@@ -1382,7 +1528,7 @@ public class TaxonServiceImpl
             Taxon newTaxon,
             boolean moveHomotypicGroup,
             SynonymType newSynonymType,
-            Reference newSecundum,
+            UUID newSecundumUuid,
             String newSecundumDetail,
             boolean keepSecundumIfUndefined) throws HomotypicalGroupChangeException {
 
@@ -1422,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);
@@ -1444,6 +1591,8 @@ public class TaxonServiceImpl
 
         result.addUpdatedObject(oldTaxon);
         result.addUpdatedObject(newTaxon);
+        result.addUpdatedObject(homotypicGroup);
+        result.addUpdatedObject(synonym);
         saveOrUpdate(oldTaxon);
         saveOrUpdate(newTaxon);
 
@@ -1477,7 +1626,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());
@@ -1545,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());
@@ -1622,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>
@@ -1814,7 +1963,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
@@ -1866,7 +2015,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);
 
@@ -1909,7 +2058,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
@@ -1964,7 +2113,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
@@ -2172,7 +2321,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(
@@ -2932,7 +3081,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());
 
@@ -2960,7 +3108,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();
@@ -2984,19 +3133,37 @@ public class TaxonServiceImpl
             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();
@@ -3010,7 +3177,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.";
@@ -3020,7 +3187,6 @@ 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())){
@@ -3264,20 +3430,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();
 
@@ -3312,13 +3464,16 @@ public class TaxonServiceImpl
        @Override
        @Transactional(readOnly = false)
        public UpdateResult swapSynonymAndAcceptedTaxon(UUID synonymUUid,
-                       UUID acceptedTaxonUuid, boolean setNameInSource) {
+                       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, setNameInSource);
+               Reference refAcc = referenceService.load(newSecAcc);
+               Reference refSyn = referenceService.load(newSecSyn);
+
+               return this.swapSynonymAndAcceptedTaxon(syn, taxon, setNameInSource, newUuidForAcceptedTaxon, secHandling, refAcc, refSyn);
        }
 
     @Override