cleanup
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index f1350b1b5aca2d73fbd90a1a2eb928233b7b6250..09ea57c2642a491ad89657ffad6c62ddf2425b4a 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;
 
@@ -96,8 +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;
@@ -126,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;
@@ -207,67 +212,106 @@ 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();
        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;
+//        }
 
-       boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(taxonName.getHomotypicalGroup());
 
+       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);
+//            }
+//        }
 
-       for(TaxonDescription description : acceptedTaxon.getDescriptions()){
-            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);
-        }
+       //nameUsedInSource
+       handleNameUsedInSourceForSwap(setNameInSource, taxonName, oldTaxonTitleCache, acceptedTaxon.getDescriptions());
 
-       acceptedTaxon.setTitleCache(null, true);
-       acceptedTaxon.getTitleCache();
-       synonym.setTitleCache(null, true);
-       synonym.getTitleCache();
-       saveOrUpdate(acceptedTaxon);
-       saveOrUpdate(synonym);
-       result.setCdmEntity(acceptedTaxon);
-       result.addUpdatedObject(synonym);
+       acceptedTaxon.resetTitleCache();
+       synonym.resetTitleCache();
 
-/*
-        synonymName.removeTaxonBase(synonym);
+       MergeResult mergeTaxon = merge(acceptedTaxon, true);
+       MergeResult mergeSynonym = merge(synonym, true);
+       result.setCdmEntity((CdmBase) mergeTaxon.getMergedEntity());
+       result.addUpdatedObject((CdmBase) mergeSynonym.getMergedEntity());
 
-        //taxonName.removeTaxonBase(acceptedTaxon);
+               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: acceptedTaxon.getSynonyms()){
+        for (Synonym syn: oldAcceptedTaxon.getSynonyms()){
             syn = HibernateProxyHelper.deproxy(syn, Synonym.class);
             synonyms.add(syn);
         }
         for (Synonym syn: synonyms){
-            acceptedTaxon.removeSynonym(syn);
+            oldAcceptedTaxon.removeSynonym(syn);
         }
-        Taxon newTaxon = acceptedTaxon.clone(true, true, false, true);
-//        newTaxon.getDescriptions().clear();
+        Taxon newTaxon = oldAcceptedTaxon.clone(true, true, false, true);
+        newTaxon.setSec(newSecAcc);
 
-        Set<TaxonDescription> descriptionsToCopy = new HashSet<>(acceptedTaxon.getDescriptions());
+        //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(synonym.isPublish());
+        newTaxon.setPublish(oldSynonym.isPublish());
         for (Synonym syn: synonyms){
             if (!syn.getName().equals(newTaxon.getName())){
                 newTaxon.addSynonym(syn, syn.getType());
@@ -280,14 +324,14 @@ public class TaxonServiceImpl
             newTaxon.removeTaxonRelation(taxonRelationship);
         }
 
-        for(TaxonRelationship taxonRelationship : acceptedTaxon.getTaxonRelations()){
+        for(TaxonRelationship taxonRelationship : oldAcceptedTaxon.getTaxonRelations()){
             Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
             Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
-            if (fromTaxon == acceptedTaxon){
+            if (fromTaxon == oldAcceptedTaxon){
                 newTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
 
-            }else if(toTaxon == acceptedTaxon){
+            }else if(toTaxon == oldAcceptedTaxon){
                fromTaxon.addTaxonRelation(newTaxon, taxonRelationship.getType(),
                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
                saveOrUpdate(fromTaxon);
@@ -295,145 +339,59 @@ 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<>( 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);
-        }
-        List<TaxonNode> nodes = new ArrayList<>(acceptedTaxon.getTaxonNodes());
+        //taxon nodes
+        List<TaxonNode> nodes = new ArrayList<>(oldAcceptedTaxon.getTaxonNodes());
         for (TaxonNode node: nodes){
-            node = HibernateProxyHelper.deproxy(node, TaxonNode.class);
+            node = HibernateProxyHelper.deproxy(node);
             TaxonNode parent = node.getParent();
-            acceptedTaxon.removeTaxonNode(node);
+            oldAcceptedTaxon.removeTaxonNode(node);
             node.setTaxon(newTaxon);
             if (parent != null){
                 parent.addChildNode(node, null, null);
             }
-
         }
-        Synonym newSynonym = synonym.clone();
+
+        //synonym
+        Synonym newSynonym = oldSynonym.clone();
         newSynonym.setName(taxonName);
-//        newSynonym.setSec(acceptedTaxon.getSec());
-        newSynonym.setPublish(acceptedTaxon.isPublish());
+        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(acceptedTaxon.getUuid(), conf, null);
-        if (synonym.isPersited()){
-            synonym.getSecSource().setSourcedTaxon(null);
-            synonym.setSecSource(null);
-            deleteResult.includeResult(deleteSynonym(synonym.getUuid(), confSyn));
+        DeleteResult deleteResult = deleteTaxon(oldAcceptedTaxon.getUuid(), conf, null);
+        if (oldSynonym.isPersited()){
+            oldSynonym.setSecSource(null);
+            deleteResult.includeResult(deleteSynonym(oldSynonym.getUuid(), confSyn));
         }
-//        saveOrUpdate(newSynonym);
-//        saveOrUpdate(newTaxon);
         result.includeResult(deleteResult);
-        */
-               return result;
-    }
-
-    @Override
-    @Transactional(readOnly = false)
-    public UpdateResult swapSynonymAndAcceptedTaxonNewUuid(Synonym synonym, Taxon acceptedTaxon, boolean setNameInSource){
-        UpdateResult result = new UpdateResult();
-      acceptedTaxon.removeSynonym(synonym);
-        TaxonName synonymName = synonym.getName();
-        TaxonName taxonName = HibernateProxyHelper.deproxy(acceptedTaxon.getName());
-
-        boolean sameHomotypicGroup = synonymName.getHomotypicalGroup().equals(taxonName.getHomotypicalGroup());
-        synonymName.removeTaxonBase(synonym);
-
-        //taxonName.removeTaxonBase(acceptedTaxon);
-
-        List<Synonym> synonyms = new ArrayList<>();
-        for (Synonym syn: acceptedTaxon.getSynonyms()){
-            syn = HibernateProxyHelper.deproxy(syn, Synonym.class);
-            synonyms.add(syn);
-        }
-        for (Synonym syn: synonyms){
-            acceptedTaxon.removeSynonym(syn);
-        }
-        Taxon newTaxon = acceptedTaxon.clone(true, true, false, true);
-//        newTaxon.getDescriptions().clear();
-
-        Set<TaxonDescription> descriptionsToCopy = new HashSet<>(acceptedTaxon.getDescriptions());
-        for (TaxonDescription description: descriptionsToCopy){
-            newTaxon.addDescription(description);
-        }
-
-        newTaxon.setName(synonymName);
-
-        newTaxon.setPublish(synonym.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 : acceptedTaxon.getTaxonRelations()){
-            Taxon fromTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
-            Taxon toTaxon = HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
-            if (fromTaxon == acceptedTaxon){
-                newTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
-                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
-
-            }else if(toTaxon == acceptedTaxon){
-               fromTaxon.addTaxonRelation(newTaxon, taxonRelationship.getType(),
-                        taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
-               saveOrUpdate(fromTaxon);
-
-            }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);
-        }
+        return result;
+    }
 
-        //Move descriptions to new taxon
-        List<TaxonDescription> descriptions = new ArrayList<>( newTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
+    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, acceptedTaxon.getTitleCache(), description.getTitleCache());
+            message = String.format(message, oldTaxonTitleCache, description.getTitleCache());
             description.setTitleCache(message, true);
             if(setNameInSource){
                 for (DescriptionElementBase element: description.getElements()){
@@ -444,52 +402,9 @@ public class TaxonServiceImpl
                     }
                 }
             }
-//            //oldTaxon.removeDescription(description, false);
- //           newTaxon.addDescription(description);
         }
-        List<TaxonNode> nodes = new ArrayList<>(acceptedTaxon.getTaxonNodes());
-        for (TaxonNode node: nodes){
-            node = HibernateProxyHelper.deproxy(node, TaxonNode.class);
-            TaxonNode parent = node.getParent();
-            acceptedTaxon.removeTaxonNode(node);
-            node.setTaxon(newTaxon);
-            if (parent != null){
-                parent.addChildNode(node, null, null);
-            }
-
-        }
-        Synonym newSynonym = synonym.clone();
-        newSynonym.setName(taxonName);
-//        newSynonym.setSec(acceptedTaxon.getSec());
-        newSynonym.setPublish(acceptedTaxon.isPublish());
-        if (sameHomotypicGroup){
-            newTaxon.addSynonym(newSynonym, SynonymType.HOMOTYPIC_SYNONYM_OF());
-        }else{
-            newTaxon.addSynonym(newSynonym, SynonymType.HETEROTYPIC_SYNONYM_OF());
-        }
-
-
-        TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
-        conf.setDeleteNameIfPossible(false);
-        SynonymDeletionConfigurator confSyn = new SynonymDeletionConfigurator();
-        confSyn.setDeleteNameIfPossible(false);
-        result.setCdmEntity(newTaxon);
-
-        DeleteResult deleteResult = deleteTaxon(acceptedTaxon.getUuid(), conf, null);
-        if (synonym.isPersited()){
-            if (synonym.getSecSource() != null){
-                synonym.getSecSource().setSourcedTaxon(null);
-            }
-            synonym.setSecSource(null);
-            deleteResult.includeResult(deleteSynonym(synonym.getUuid(), confSyn));
-        }
-        result.includeResult(deleteResult);
-
-        return result;
     }
 
-
-
     @Override
     @Transactional(readOnly = false)
     public UpdateResult changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, Reference newSecRef, String microRef, SecReferenceHandlingEnum secHandling, boolean deleteSynonym) {
@@ -505,9 +420,7 @@ public class TaxonServiceImpl
             result.setAbort();
             return result;
         }
-        if (secHandling != null && secHandling.equals(SecReferenceHandlingEnum.KeepAlways)){
-            newSecRef = synonym.getSec();
-        }
+
         Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, newSecRef, microRef);
         newAcceptedTaxon.setPublish(synonym.isPublish());
         dao.save(newAcceptedTaxon);
@@ -516,7 +429,7 @@ public class TaxonServiceImpl
         List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
 
         for (Synonym heteroSynonym : heteroSynonyms){
-            if (secHandling == null || !secHandling.equals(SecReferenceHandlingEnum.KeepAlways)){
+            if (secHandling == null){
                 heteroSynonym.setSec(newSecRef);
             }
             if (synonym.equals(heteroSynonym)){
@@ -562,28 +475,25 @@ public class TaxonServiceImpl
             case AlwaysDelete:
                 newSecRef = null;
                 break;
-            case KeepAlways:
-                break;
             case UseNewParentSec:
                 newSecRef = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
                 break;
-            case KeepWhenSame:
+            case KeepOrWarn:
                 Reference parentSec = newParentNode.getTaxon() != null? newParentNode.getTaxon().getSec(): null;
                 Reference synSec = synonym.getSec();
-                if (parentSec != null && synSec != null && parentSec.equals(synSec)){
-                    newSecRef = synonym.getSec();
+                if (synSec != null ){
+                    newSecRef = CdmBase.deproxy(synSec);
                 }else{
                     newSecRef = CdmBase.deproxy(referenceService.load(newSec));
                 }
                 break;
-            case WarningSelect:
+            case KeepOrSelect:
                 newSecRef = CdmBase.deproxy(referenceService.load(newSec));
-
+                break;
             default:
                 break;
         }
 
-
         result =  changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, newSecRef, microReference, secHandling, deleteSynonym);
         Taxon newTaxon = (Taxon)result.getCdmEntity();
 
@@ -593,7 +503,6 @@ public class TaxonServiceImpl
         result.addUpdatedObject(acceptedTaxon);
         result.setCdmEntity(newNode);
         return result;
-
     }
 
     @Override
@@ -1069,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<>();
@@ -1081,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) {
@@ -1114,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
@@ -1148,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()) {
@@ -1171,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);
@@ -1437,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;
@@ -1579,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,
@@ -1587,7 +1517,7 @@ public class TaxonServiceImpl
             SynonymType newSynonymType) throws HomotypicalGroupChangeException {
         return moveSynonymToAnotherTaxon(oldSynonym, newTaxon, moveHomotypicGroup,
                 newSynonymType,
-                oldSynonym.getSec()!= null?oldSynonym.getSec().getUuid():null,
+                oldSynonym.getSec()!= null? oldSynonym.getSec().getUuid(): null,
                 oldSynonym.getSecMicroReference(),
                 true);
     }
@@ -1661,6 +1591,8 @@ public class TaxonServiceImpl
 
         result.addUpdatedObject(oldTaxon);
         result.addUpdatedObject(newTaxon);
+        result.addUpdatedObject(homotypicGroup);
+        result.addUpdatedObject(synonym);
         saveOrUpdate(oldTaxon);
         saveOrUpdate(newTaxon);
 
@@ -1694,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());
@@ -1762,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());
@@ -1839,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>
@@ -1928,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 " +
@@ -2031,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
@@ -2083,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);
 
@@ -2126,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
@@ -2181,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
@@ -2389,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(
@@ -3149,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());
 
@@ -3215,10 +3147,11 @@ public class TaxonServiceImpl
         }else{
             SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;
             result = isDeletableForSynonym(references, synonymConfig);
-            if (synonymConfig.isDeleteNameIfPossible()){
+            if (synonymConfig.isDeleteNameIfPossible() && taxonBase.getName() != null){
                 DeleteResult nameResult = nameService.isDeletable(taxonBase.getName().getUuid(), synonymConfig.getNameDeletionConfig(), taxonBase.getUuid());
                 if (!nameResult.isOk()){
                     result.addExceptions(nameResult.getExceptions());
+
                 }
             }
         }
@@ -3497,20 +3430,6 @@ public class TaxonServiceImpl
         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, result);
     }
 
-    @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
        public UpdateResult moveFactualDateToAnotherTaxon(UUID fromTaxonUuid, UUID toTaxonUuid){
                UpdateResult result = new UpdateResult();
@@ -3546,13 +3465,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