change exception type for "changeSynonymToAccepted" from IllegalState to HomotypicalC...
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index bc9b06456791b457d29b95ad3683b1094d0b3068..a373e59330cf7c92e3a149cb13ed15db873e58a7 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$\r
 /**\r
 * Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
+* European Distributed Institute of Taxonomy\r
 * http://www.e-taxonomy.eu\r
-* \r
+*\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
 */\r
 package eu.etaxonomy.cdm.api.service;\r
 \r
 import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-\r
 import java.util.HashSet;\r
 import java.util.List;\r
-import java.util.Map;\r
 import java.util.Set;\r
-import java.util.TreeMap;\r
 import java.util.UUID;\r
-import java.util.Map.Entry;\r
+\r
+import junit.framework.Assert;\r
 \r
 import org.apache.log4j.Logger;\r
-import org.hibernate.Hibernate;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Service;\r
-import org.springframework.transaction.TransactionStatus;\r
+import org.springframework.transaction.annotation.Propagation;\r
 import org.springframework.transaction.annotation.Transactional;\r
 \r
 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;\r
+import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;\r
+import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;\r
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
-import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
+import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
-import eu.etaxonomy.cdm.model.description.CommonTaxonName;\r
-import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
 import eu.etaxonomy.cdm.model.media.Media;\r
 import eu.etaxonomy.cdm.model.media.MediaRepresentation;\r
+import eu.etaxonomy.cdm.model.media.MediaUtils;\r
 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
-import eu.etaxonomy.cdm.model.reference.ReferenceBase;\r
+import eu.etaxonomy.cdm.model.reference.Reference;\r
+import eu.etaxonomy.cdm.model.taxon.Classification;\r
 import eu.etaxonomy.cdm.model.taxon.Synonym;\r
 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;\r
 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;\r
@@ -56,52 +56,33 @@ import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\r
-import eu.etaxonomy.cdm.model.taxon.TaxonomicTree;\r
-import eu.etaxonomy.cdm.persistence.dao.BeanInitializer;\r
 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;\r
-import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;\r
 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;\r
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;\r
-import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;\r
-import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonomicTreeDao;\r
 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
 import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
+import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;\r
 \r
 \r
+/**\r
+ * @author a.kohlbecker\r
+ * @date 10.09.2010\r
+ *\r
+ */\r
 @Service\r
-@Transactional(readOnly = true)\r
-public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService {\r
-       private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
+@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\r
+public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{\r
+    private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
 \r
        @Autowired\r
        private ITaxonNameDao nameDao;\r
-       @Autowired\r
-       private ITaxonomicTreeDao taxonTreeDao;\r
-       @Autowired\r
-       private ITaxonNodeDao taxonNodeDao;\r
-       @Autowired\r
-       private ITaxonDao taxonDao;\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * remove commented out dao\r
-        */\r
-//     @Autowired\r
-//     @Qualifier("nonViralNameDaoHibernateImpl")\r
-//     private INonViralNameDao nonViralNameDao;\r
+\r
        @Autowired\r
        private IOrderedTermVocabularyDao orderedVocabularyDao;\r
-       @Autowired\r
-       private IDescriptionDao descriptionDao;\r
-       @Autowired\r
-       private BeanInitializer defaultBeanInitializer;\r
-       \r
-       private Comparator<? super TaxonNode> taxonNodeComparator;\r
-       @Autowired\r
-       public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){\r
-               this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;\r
-       }\r
 \r
+       @Autowired\r
+       private INameService nameService;\r
        \r
        /**\r
         * Constructor\r
@@ -109,743 +90,873 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
        public TaxonServiceImpl(){\r
                if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }\r
        }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * find\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getTaxonByUuid(java.util.UUID)\r
-        */\r
-       public TaxonBase getTaxonByUuid(UUID uuid) {\r
-               return super.findByUuid(uuid);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * rename searchByName ?\r
+     */\r
+    public List<TaxonBase> searchTaxaByName(String name, Reference sec) {\r
+        return dao.getTaxaByName(name, sec);\r
+    }\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * list(Synonym.class, ...)\r
+     *  (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)\r
+     */\r
+    public List<Synonym> getAllSynonyms(int limit, int start) {\r
+        return dao.getAllSynonyms(limit, start);\r
+    }\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * list(Taxon.class, ...)\r
+     *  (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)\r
+     */\r
+    public List<Taxon> getAllTaxa(int limit, int start) {\r
+        return dao.getAllTaxa(limit, start);\r
+    }\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * merge with getRootTaxa(Reference sec, ..., ...)\r
+     *  (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)\r
+     */\r
+    public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {\r
+        if (cdmFetch == null){\r
+            cdmFetch = CdmFetch.NO_FETCH();\r
+        }\r
+        return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);\r
+    }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)\r
+     */\r
+    public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {\r
+        return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)\r
+     */\r
+    public List<RelationshipBase> getAllRelationships(int limit, int start){\r
+        return dao.getAllRelationships(limit, start);\r
+    }\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?\r
+     */\r
+    @Deprecated\r
+    public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {\r
+\r
+        String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";\r
+        UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);\r
+        OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =\r
+            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);\r
+        return taxonRelTypeVocabulary;\r
+    }\r
+\r
+    \r
+    \r
+       /*\r
         * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getTaxonNodeByUuid(java.util.UUID)\r
-        */\r
-       public TaxonNode getTaxonNodeByUuid(UUID uuid) {\r
-               return taxonNodeDao.findByUuid(uuid);\r
-       }\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        */\r
-       public TaxonNode loadTaxonNodeByTaxon(Taxon taxon, UUID taxonomicTreeUuid, List<String> propertyPaths){\r
-               TaxonomicTree tree = taxonTreeDao.load(taxonomicTreeUuid);\r
-               TaxonNode node = tree.getNode(taxon);\r
-               defaultBeanInitializer.initialize(node, propertyPaths);\r
-               return node;\r
-       }\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService \r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)\r
         */\r
-       public List<TaxonNode> loadRankSpecificRootNodes(TaxonomicTree taxonomicTree, Rank rank, List<String> propertyPaths){\r
-               TaxonomicTree tree = taxonTreeDao.load(taxonomicTree.getUuid());\r
-               \r
-               List<TaxonNode> rootNodes = tree.getRankSpecificRootNodes(rank);\r
-               //sort nodes by TaxonName\r
+       @Transactional(readOnly = false)\r
+       public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
                \r
-               Collections.sort(rootNodes, taxonNodeComparator);\r
+               TaxonNameBase<?,?> synonymName = synonym.getName();\r
+               synonymName.removeTaxonBase(synonym);\r
+               TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();\r
+               taxonName.removeTaxonBase(acceptedTaxon);\r
                \r
-               // initialize all nodes\r
-               defaultBeanInitializer.initializeAll(rootNodes, propertyPaths);\r
+               synonym.setName(taxonName);\r
+               acceptedTaxon.setName(synonymName);\r
                \r
-               return rootNodes;\r
+               // the accepted taxon needs a new uuid because the concept has changed\r
+               // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"\r
+               //acceptedTaxon.setUuid(UUID.randomUUID());\r
        }\r
+\r
        \r
-       /**\r
-     * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#loadTreeBranchTo(eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.model.name.Rank, java.util.List)\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\r
         */\r
-       public List<TaxonNode> loadTreeBranchTo(TaxonNode taxonNode, Rank baseRank, List<String> propertyPaths){\r
+       //TODO correct delete handling still needs to be implemented / checked\r
+       @Override\r
+       @Transactional(readOnly = false)\r
+       public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{\r
                \r
-               TaxonNode thisNode = taxonNodeDao.load(taxonNode.getUuid(), propertyPaths);\r
-               List<TaxonNode> pathToRoot = new ArrayList<TaxonNode>();\r
-               pathToRoot.add(thisNode);\r
+               TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();\r
+               TaxonNameBase<?,?> synonymName = synonym.getName();\r
+               HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();\r
                \r
-               TaxonNode parentNode = thisNode.getParent();\r
-               while(parentNode != null){\r
-                       Rank parentNodeRank = parentNode.getTaxon().getName().getRank();\r
-                       // stop if the next parent is higher than the baseRank\r
-                       if(baseRank != null && baseRank.isLower(parentNodeRank)){\r
-                               break;\r
-                       }\r
-                       pathToRoot.add(parentNode);\r
-                       parentNode = parentNode.getParent();\r
+               //check synonym is not homotypic\r
+               if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){\r
+                       String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";\r
+                       throw new HomotypicalGroupChangeException(message);\r
                }\r
                \r
-               // initialize and invert order of nodes in list\r
-               defaultBeanInitializer.initializeAll(pathToRoot, propertyPaths);\r
-               Collections.reverse(pathToRoot);\r
-               \r
-               return pathToRoot;\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        */\r
-       public List<TaxonNode> loadTreeBranchToTaxon(Taxon taxon, TaxonomicTree taxonomicTree, Rank baseRank, List<String> propertyPaths){\r
-               TaxonomicTree tree = taxonTreeDao.load(taxonomicTree.getUuid());\r
-               taxon = (Taxon)dao.load(taxon.getUuid());\r
-               TaxonNode node = tree.getNode(taxon);\r
-               if(node == null){\r
-                       logger.warn("The specified tacon is not found in the given tree.");\r
-                       return null;\r
-               }\r
-               return loadTreeBranchTo(node, baseRank, propertyPaths);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        */\r
-       public List<TaxonNode> loadChildNodesOfTaxon(Taxon taxon, TaxonomicTree taxonomicTree, List<String> propertyPaths){\r
-               TaxonomicTree tree = taxonTreeDao.load(taxonomicTree.getUuid());\r
-               taxon = (Taxon)dao.load(taxon.getUuid());\r
-               List<TaxonNode> childNodes = new ArrayList<TaxonNode>();\r
-               childNodes.addAll(tree.getNode(taxon).getChildNodes());\r
-               Collections.sort(childNodes, taxonNodeComparator);\r
-               defaultBeanInitializer.initializeAll(childNodes, propertyPaths);\r
-               return childNodes;\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * save\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public UUID saveTaxon(TaxonBase taxon) {\r
-               return super.saveCdmObject(taxon);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxonNode(eu.etaxonomy.cdm.model.taxon.TaxonNode)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public UUID saveTaxonNode(TaxonNode taxonNode) {\r
-               return taxonNodeDao.save(taxonNode);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * this method seems redundant, remove\r
-        * @param taxon\r
-        * @param txStatus\r
-        * @return\r
-        */\r
-       //@Transactional(readOnly = false)\r
-       public UUID saveTaxon(TaxonBase taxon, TransactionStatus txStatus) {\r
-               //return super.saveCdmObject(taxon, txStatus);\r
-               return super.saveCdmObject(taxon);\r
-       }\r
-       \r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * save(Set<Taxon> taxa)\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxonAll(java.util.Collection)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public Map<UUID, ? extends TaxonBase> saveTaxonAll(Collection<? extends TaxonBase> taxonCollection){\r
-               return saveCdmObjectAll(taxonCollection);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        */\r
-       public Map<UUID, TaxonNode> saveTaxonNodeAll(\r
-                       Collection<TaxonNode> taxonNodeCollection) {\r
-               return taxonNodeDao.saveAll(taxonNodeCollection);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * delete\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#removeTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public UUID removeTaxon(TaxonBase taxon) {\r
-               return super.removeCdmObject(taxon);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * rename searchByName ? \r
-        */\r
-       public List<TaxonBase> searchTaxaByName(String name, ReferenceBase sec) {\r
-               return dao.getTaxaByName(name, sec);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * list\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxonBases(int, int)\r
-        */\r
-       public List<TaxonBase> getAllTaxonBases(int limit, int start){\r
-               return dao.list(limit, start);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * list\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)\r
-        */\r
-       public List<Taxon> getAllTaxa(int limit, int start){\r
-               return dao.getAllTaxa(limit, start);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * list(Synonym.class, ...)\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)\r
-        */\r
-       public List<Synonym> getAllSynonyms(int limit, int start) {\r
-               return dao.getAllSynonyms(limit, start);\r
-       }\r
-\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * move to taxonTreeService\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxonomicTrees(int, int)\r
-        * \r
-        */\r
-       @Deprecated\r
-       public List<TaxonomicTree> getAllTaxonomicTrees(int limit, int start) {\r
-               return taxonTreeDao.list(limit, start);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * taxonTreeService.list\r
-        */\r
-       public List<TaxonomicTree> listTaxonomicTrees(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-               return taxonTreeDao.list(limit, start, orderHints, propertyPaths);\r
-       }       \r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * taxonTreeService.find\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getTaxonomicTreeByUuid(java.util.UUID)\r
-        */\r
-       public TaxonomicTree getTaxonomicTreeByUuid(UUID uuid){\r
-               return taxonTreeDao.findByUuid(uuid);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * taxonTreeService.delete\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#removeTaxonomicTree(java.util.UUID)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public UUID removeTaxonomicTree(TaxonomicTree taxonomicTree) {\r
-               return taxonTreeDao.delete(taxonomicTree);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * taxonTreeService.save\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxonomicTree(eu.etaxonomy.cdm.model.taxon.TaxonomicTree)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public UUID saveTaxonomicTree(TaxonomicTree tree){\r
-               return taxonTreeDao.saveOrUpdate(tree);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * merge with getRootTaxa(ReferenceBase sec, ..., ...)\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase)\r
-        */\r
-       public List<Taxon> getRootTaxa(ReferenceBase sec){\r
-               return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * merge with getRootTaxa(ReferenceBase sec, ..., ...)\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean)\r
-        */\r
-       public List<Taxon> getRootTaxa(ReferenceBase sec, CdmFetch cdmFetch, boolean onlyWithChildren) {\r
-               if (cdmFetch == null){\r
-                       cdmFetch = CdmFetch.NO_FETCH();\r
-               }\r
-               return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * merge with getRootTaxa(ReferenceBase sec, ..., ...)\r
-        *  (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)\r
-        */\r
-       public List<Taxon> getRootTaxa(ReferenceBase sec, boolean onlyWithChildren,\r
-                       boolean withMisapplications) {\r
-               return dao.getRootTaxa(sec, null, onlyWithChildren, withMisapplications);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)\r
-        */\r
-       public List<Taxon> getRootTaxa(Rank rank, ReferenceBase sec, boolean onlyWithChildren,\r
-                       boolean withMisapplications, List<String> propertyPaths) {\r
-               return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);\r
-       }\r
-\r
-       public List<RelationshipBase> getAllRelationships(int limit, int start){\r
-               return dao.getAllRelationships(limit, start);\r
-       }\r
-       \r
-       \r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ? \r
-        */\r
-       @Deprecated\r
-       public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {\r
+               Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());\r
                \r
-               String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";\r
-               UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);\r
-               OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary = \r
-                       (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);\r
-               return taxonRelTypeVocabulary;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public Synonym makeTaxonSynonym(Taxon oldTaxon, Taxon newAcceptedTaxon, SynonymRelationshipType synonymRelationshipType, ReferenceBase citation, String citationMicroReference) {\r
-               if (oldTaxon == null || newAcceptedTaxon == null || oldTaxon.getName() == null){\r
-                       throw new IllegalArgumentException();\r
-               }\r
+               SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+               List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);\r
                \r
-               // Move oldTaxon to newTaxon\r
-               TaxonNameBase<?,?> synonymName = oldTaxon.getName();\r
-               if (synonymRelationshipType == null){\r
-                       if (synonymName.isHomotypic(newAcceptedTaxon.getName())){\r
-                               synonymRelationshipType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+               for (Synonym heteroSynonym : heteroSynonyms){\r
+                       if (synonym.equals(heteroSynonym)){\r
+                               acceptedTaxon.removeSynonym(heteroSynonym, false);\r
                        }else{\r
-                               //TODO synonymType \r
-                               synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+                               //move synonyms in same homotypic group to new accepted taxon\r
+                               heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);\r
                        }\r
                }\r
-               SynonymRelationship synRel = newAcceptedTaxon.addSynonymName(synonymName, synonymRelationshipType, citation, citationMicroReference);\r
-               \r
-               //Move Synonym Relations to new Taxon\r
-               for(SynonymRelationship synRelation : oldTaxon.getSynonymRelations()){\r
-                       newAcceptedTaxon.addSynonym(synRelation.getSynonym(), synRelation.getType(), \r
-                                       synRelation.getCitation(), synRelation.getCitationMicroReference());\r
-               }\r
 \r
-               //Move Taxon RelationShips to new Taxon\r
-               Set<TaxonRelationship> removableTaxonRels = new HashSet<TaxonRelationship>();\r
-               for(TaxonRelationship taxonRelation : oldTaxon.getTaxonRelations()){\r
-                       //CHILDREN\r
-                       if (taxonRelation.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){\r
-                               if (taxonRelation.getFromTaxon() == oldTaxon){\r
-                                       removableTaxonRels.add(taxonRelation);\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addTaxonomicChild(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       removableTaxonRels.add(taxonRelation);\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else{\r
-                                       logger.warn("Taxon is not part of its own Taxonrelationship");\r
-                               }\r
+               //synonym.getName().removeTaxonBase(synonym);\r
+               //TODO correct delete handling still needs to be implemented / checked\r
+               if (deleteSynonym){\r
+//                     deleteSynonym(synonym, taxon, false);\r
+                       try {\r
+                               this.dao.flush();\r
+                               this.delete(synonym);\r
+                               \r
+                       } catch (Exception e) {\r
+                               logger.info("Can't delete old synonym from database");\r
                        }\r
-                       //MISAPPLIED NAMES\r
-                       if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){\r
-                               if (taxonRelation.getFromTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       removableTaxonRels.add(taxonRelation);\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       removableTaxonRels.add(taxonRelation);\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else{\r
-                                       logger.warn("Taxon is not part of its own Taxonrelationship");\r
-                               }\r
-                       }\r
-                       //Concept Relationships\r
-                       //FIXME implement\r
-//                     if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIEDNAMEFOR())){\r
-//                             if (taxonRelation.getFromTaxon() == oldTaxon){\r
-//                                     newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-//                             removableTaxonRels.add(taxonRelation);\r
-//                             }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-//                                     newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-//                     removableTaxonRels.add(taxonRelation);\r
-//                             }else{\r
-//                                     logger.warn("Taxon is not part of its own Taxonrelationship");\r
-//                             }\r
-//                     }\r
                }\r
                \r
-               for(TaxonRelationship taxonRel : removableTaxonRels) {\r
-                       oldTaxon.removeTaxonRelation(taxonRel);\r
-               }\r
-               \r
-               //Move Descriptions to new Taxon\r
-               for(TaxonDescription taxDescription : oldTaxon.getDescriptions()){\r
-                       newAcceptedTaxon.addDescription(taxDescription);\r
-               }\r
-               //delete old Taxon\r
-               this.dao.saveOrUpdate(newAcceptedTaxon);\r
-//             FIXME implement\r
-//             this.dao.delete(oldTaxon);\r
-               \r
-               //return\r
-//             this.dao.flush();\r
-               return synRel.getSynonym();\r
+               return newAcceptedTaxon;\r
        }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)\r
-        */\r
-       @Transactional(readOnly = false)\r
-       public void makeSynonymAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, SynonymRelationshipType synonymRelationshipType){\r
+       \r
+       \r
+       public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){\r
                \r
-               // create a new synonym with the old acceptedName\r
-               TaxonNameBase oldAcceptedTaxonName = acceptedTaxon.getName();\r
+               // Get name from synonym\r
+               TaxonNameBase<?, ?> synonymName = synonym.getName();\r
                \r
-               // remove synonym from oldAcceptedTaxon\r
-               acceptedTaxon.removeSynonym(synonym);\r
+               // remove synonym from taxon\r
+               toTaxon.removeSynonym(synonym);\r
                \r
-               // make synonym name the accepted taxons name\r
-               TaxonNameBase newAcceptedTaxonName = synonym.getName();\r
-               acceptedTaxon.setName(newAcceptedTaxonName);\r
+               // Create a taxon with synonym name\r
+               Taxon fromTaxon = Taxon.NewInstance(synonymName, null);\r
                \r
-               // add the new synonym to the acceptedTaxon\r
-               if(synonymRelationshipType == null){\r
-                       synonymRelationshipType = SynonymRelationshipType.SYNONYM_OF();\r
-               }\r
+               // Add taxon relation \r
+               fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);\r
+                               \r
+               // since we are swapping names, we have to detach the name from the synonym completely. \r
+               // Otherwise the synonym will still be in the list of typified names.\r
+               synonym.getName().removeTaxonBase(synonym);\r
                \r
-               acceptedTaxon.addSynonymName(oldAcceptedTaxonName, synonymRelationshipType);\r
+               return fromTaxon;\r
        }\r
+       \r
 \r
-       public void generateTitleCache() {\r
-               generateTitleCache(true);\r
-       }\r
-       //TODO\r
-       public void generateTitleCache(boolean forceProtected) {\r
-               logger.warn("generateTitleCache not yet fully implemented!");\r
-//             for (TaxonBase tb : taxonDao.getAllTaxa(null,null)){\r
-//                     logger.warn("Old taxon title: " + tb.getTitleCache());\r
-//                     if (forceProtected || !tb.isProtectedTitleCache() ){\r
-//                             tb.setTitleCache(tb.generateTitle(), false);\r
-//                             taxonDao.update(tb);\r
-//                             logger.warn("New title: " + tb.getTitleCache());\r
-//                     }\r
-//             }\r
-               \r
-       }\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeHomotypicalGroupOfSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.name.HomotypicalGroup, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)\r
+     */\r
+    @Transactional(readOnly = false)\r
+    @Override\r
+    public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,\r
+                        boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){\r
+        // Get synonym name\r
+        TaxonNameBase synonymName = synonym.getName();\r
+        HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();\r
+\r
+\r
+        // Switch groups\r
+        oldHomotypicalGroup.removeTypifiedName(synonymName);\r
+        newHomotypicalGroup.addTypifiedName(synonymName);\r
+\r
+        //remove existing basionym relationships\r
+        synonymName.removeBasionyms();\r
+\r
+        //add basionym relationship\r
+        if (setBasionymRelationIfApplicable){\r
+            Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();\r
+            for (TaxonNameBase basionym : basionyms){\r
+                synonymName.addBasionym(basionym);\r
+            }\r
+        }\r
+\r
+        //set synonym relationship correctly\r
+//                     SynonymRelationship relToTaxon = null;\r
+        boolean relToTargetTaxonExists = false;\r
+        Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();\r
+        for (SynonymRelationship rel : existingRelations){\r
+            Taxon acceptedTaxon = rel.getAcceptedTaxon();\r
+            boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);\r
+            HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();\r
+            boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);\r
+            SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+            rel.setType(newRelationType);\r
+            //TODO handle citation and microCitation\r
+\r
+            if (isTargetTaxon){\r
+                relToTargetTaxonExists = true;\r
+            }else{\r
+                if (removeFromOtherTaxa){\r
+                    acceptedTaxon.removeSynonym(synonym, false);\r
+                }else{\r
+                    //do nothing\r
+                }\r
+            }\r
+        }\r
+        if (targetTaxon != null &&  ! relToTargetTaxonExists ){\r
+            Taxon acceptedTaxon = targetTaxon;\r
+            HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();\r
+            boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);\r
+            SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+            //TODO handle citation and microCitation\r
+            Reference citation = null;\r
+            String microCitation = null;\r
+            acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);\r
+        }\r
+\r
+    }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)\r
+     */\r
+    @Override\r
+    public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {\r
+        if (clazz == null){\r
+            clazz = TaxonBase.class;\r
+        }\r
+        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);\r
+    }\r
+\r
+    @Autowired\r
+    protected void setDao(ITaxonDao dao) {\r
+        this.dao = dao;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)\r
+     */\r
+    public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {\r
+        Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);\r
 \r
-       @Autowired\r
-       protected void setDao(ITaxonDao dao) {\r
-               this.dao = dao;\r
-       }\r
+        List<TaxonBase> results = new ArrayList<TaxonBase>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);\r
+        }\r
+\r
+        return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
 \r
-       public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial,      String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)\r
+     */\r
+    public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial,  String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {\r
         Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);\r
-               \r
-               List<TaxonBase> results = new ArrayList<TaxonBase>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber); \r
-               }\r
-               \r
-               return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
 \r
-       public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
-               Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
-               \r
-               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo); \r
-               }\r
-               return results;\r
-       }\r
-       \r
-       public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        List<TaxonBase> results = new ArrayList<TaxonBase>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);\r
+        }\r
+\r
+        return results;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
-               \r
-               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo); \r
-               }\r
-               return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
-       \r
-       public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
-               Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
-               \r
-               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom); \r
-               }\r
-               return results;\r
-       }\r
-       \r
-       public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+\r
+        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);\r
+        }\r
+        return results;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
+\r
+        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);\r
+        }\r
+        return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
+        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
+\r
+        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);\r
+        }\r
+        return results;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
-               \r
-               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom); \r
-               }\r
-               return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
 \r
-       public Pager<SynonymRelationship> getSynonyms(Taxon taxon,      SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);\r
+        }\r
+        return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
         Integer numberOfResults = dao.countSynonyms(taxon, type);\r
+\r
+        List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);\r
+        }\r
+\r
+        return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    public Pager<SynonymRelationship> getSynonyms(Synonym synonym,     SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        Integer numberOfResults = dao.countSynonyms(synonym, type);\r
                \r
                List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
                if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths); \r
+                   results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);\r
                }\r
-               \r
-               return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
-       \r
+\r
+        return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
+\r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
+        */\r
        public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){\r
                Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
                return t.getHomotypicSynonymsByHomotypicGroup();\r
        }\r
        \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
+        */\r
        public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){\r
                Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
-               List<HomotypicalGroup> hsgl = t.getHeterotypicSynonymyGroups();\r
-               List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(hsgl.size());\r
-               for(HomotypicalGroup hsg : hsgl){\r
-                       heterotypicSynonymyGroups.add(hsg.getSynonymsInGroup(t.getSec()));\r
+               List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();\r
+               List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());\r
+               for(HomotypicalGroup homotypicalGroup : homotypicalGroups){\r
+                       heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));\r
                }\r
                return heterotypicSynonymyGroups;\r
        }\r
 \r
-       public Pager<TaxonBase> search(Class<? extends TaxonBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-        Integer numberOfResults = dao.count(clazz,queryString);\r
+       public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){\r
                \r
-               List<TaxonBase> results = new ArrayList<TaxonBase>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.search(clazz,queryString, pageSize, pageNumber, orderHints, propertyPaths); \r
+               List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();\r
+               Class<? extends TaxonBase> clazz = null;\r
+               if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {\r
+                       clazz = TaxonBase.class;\r
+                       //propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+                       //propertyPath.addAll(configurator.getSynonymPropertyPath());\r
+               } else if(configurator.isDoTaxa()) {\r
+                       clazz = Taxon.class;\r
+                       //propertyPath = configurator.getTaxonPropertyPath();\r
+               } else if (configurator.isDoSynonyms()) {\r
+                       clazz = Synonym.class;\r
+                       //propertyPath = configurator.getSynonymPropertyPath();\r
                }\r
                \r
-               return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);\r
+               \r
+               result = dao.getTaxaByNameForEditor(clazz, configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
+               return result;\r
        }\r
 \r
-       \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)\r
+        */\r
        public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {\r
                \r
                List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();\r
                int numberOfResults = 0; // overall number of results (as opposed to number of results per page)\r
                List<TaxonBase> taxa = null; \r
 \r
-               // Taxa and synonyms\r
-               long numberTaxaResults = 0L;\r
-               \r
-               Class<? extends TaxonBase> clazz = null;\r
-               if (configurator.isDoTaxa() && configurator.isDoSynonyms()) {\r
-                       clazz = TaxonBase.class;\r
-               } else if(configurator.isDoTaxa()) {\r
-                       clazz = Taxon.class;\r
-               } else if (configurator.isDoSynonyms()) {\r
-                       clazz = Synonym.class;\r
+        // Taxa and synonyms\r
+        long numberTaxaResults = 0L;\r
+\r
+        Class<? extends TaxonBase> clazz = null;\r
+        List<String> propertyPath = new ArrayList<String>();\r
+        if(configurator.getTaxonPropertyPath() != null){\r
+            propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+        }\r
+        if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {\r
+            clazz = TaxonBase.class;\r
+            //propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+            //propertyPath.addAll(configurator.getSynonymPropertyPath());\r
+        } else if(configurator.isDoTaxa()) {\r
+            clazz = Taxon.class;\r
+            //propertyPath = configurator.getTaxonPropertyPath();\r
+        } else if (configurator.isDoSynonyms()) {\r
+            clazz = Synonym.class;\r
+            //propertyPath = configurator.getSynonymPropertyPath();\r
+        }\r
+\r
+        if(clazz != null){\r
+            if(configurator.getPageSize() != null){ // no point counting if we need all anyway\r
+                numberTaxaResults =\r
+                    dao.countTaxaByName(clazz,\r
+                        configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),\r
+                        configurator.getNamedAreas());\r
+            }\r
+\r
+            if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results\r
+                taxa = dao.getTaxaByName(clazz,\r
+                    configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),\r
+                    configurator.getNamedAreas(), configurator.getPageSize(),\r
+                    configurator.getPageNumber(), propertyPath);\r
+            }\r
+        }\r
+\r
+        if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }\r
+\r
+        if(taxa != null){\r
+            results.addAll(taxa);\r
+        }\r
+\r
+        numberOfResults += numberTaxaResults;\r
+\r
+        // Names without taxa\r
+        if (configurator.isDoNamesWithoutTaxa()) {\r
+            int numberNameResults = 0;\r
+\r
+            List<? extends TaxonNameBase<?,?>> names =\r
+                nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),\r
+                        configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());\r
+            if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }\r
+            if (names.size() > 0) {\r
+                for (TaxonNameBase<?,?> taxonName : names) {\r
+                    if (taxonName.getTaxonBases().size() == 0) {\r
+                        results.add(taxonName);\r
+                        numberNameResults++;\r
+                    }\r
+                }\r
+                if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }\r
+                numberOfResults += numberNameResults;\r
+            }\r
+        }\r
+\r
+        // Taxa from common names\r
+\r
+        if (configurator.isDoTaxaByCommonNames()) {\r
+            taxa = null;\r
+            numberTaxaResults = 0;\r
+            if(configurator.getPageSize() != null){// no point counting if we need all anyway\r
+                numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
+            }\r
+            if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){\r
+                taxa = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());\r
+            }\r
+            if(taxa != null){\r
+                results.addAll(taxa);\r
+            }\r
+            numberOfResults += numberTaxaResults;\r
+\r
+        }\r
+\r
+       return new DefaultPagerImpl<IdentifiableEntity>\r
+            (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);\r
+    }\r
+\r
+    public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){\r
+        return dao.getUuidAndTitleCache();\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])\r
+     */\r
+    public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){\r
+        List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();\r
+        taxon = (Taxon)dao.load(taxon.getUuid());\r
+        Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
+        for (TaxonDescription taxDesc: descriptions){\r
+            Set<DescriptionElementBase> elements = taxDesc.getElements();\r
+            for (DescriptionElementBase descElem: elements){\r
+                for(Media media : descElem.getMedia()){\r
+\r
+                    //find the best matching representation\r
+                    medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));\r
+\r
+                }\r
+            }\r
+        }\r
+        return medRep;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)\r
+     */\r
+    public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
+        return this.dao.findById(listOfIDs);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()\r
+     */\r
+    public int countAllRelationships() {\r
+        return this.dao.countAllRelationships();\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#createAllInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon)\r
+     */\r
+    public List<Synonym> createAllInferredSynonyms(Classification tree,\r
+            Taxon taxon) {\r
+\r
+        return this.dao.createAllInferredSynonyms(taxon, tree);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#createInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType)\r
+     */\r
+    public List<Synonym> createInferredSynonyms(Classification tree, Taxon taxon, SynonymRelationshipType type) {\r
+        return this.dao.createInferredSynonyms(taxon, tree, type);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)\r
+     */\r
+    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {\r
+        return this.dao.findIdenticalTaxonNames(propertyPath);\r
+    }\r
+\r
+\r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)\r
+        */\r
+       @Transactional(readOnly = false)\r
+       @Override\r
+       public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {\r
+               if (synonym == null){\r
+                       return;\r
                }\r
-               \r
-               if(clazz != null){\r
-                       numberTaxaResults = \r
-                               dao.countTaxaByName(clazz, \r
-                                       configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(),\r
-                                       configurator.getNamedAreas());\r
-                       if(numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results\r
-                               taxa = dao.getTaxaByName(clazz, \r
-                                       configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(),\r
-                                       configurator.getNamedAreas(), configurator.getPageSize(), \r
-                                       configurator.getPageNumber(), configurator.getTaxonPropertyPath());\r
-                       }\r
+               synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\r
+               \r
+               //remove synonymRelationship\r
+               Set<Taxon> taxonSet = new HashSet<Taxon>();\r
+               if (taxon != null){\r
+                       taxonSet.add(taxon);\r
+               }else{\r
+                       taxonSet.addAll(synonym.getAcceptedTaxa());\r
                }\r
-\r
-               if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }\r
-               \r
-               if(taxa != null){\r
-                       results.addAll(taxa);\r
+               for (Taxon relatedTaxon : taxonSet){\r
+//                     dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
+                       relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);\r
                }\r
+               this.saveOrUpdate(synonym);\r
                \r
-               numberOfResults += numberTaxaResults;\r
-               \r
-               // Names without taxa \r
+               //TODO remove name from homotypical group?\r
                \r
-               if (configurator.isDoNamesWithoutTaxa()) {\r
-            int numberNameResults = 0;\r
-            //FIXME implement search by area\r
-                       List<? extends TaxonNameBase<?,?>> names = \r
-                               nameDao.findByName(configurator.getSearchString(), configurator.getMatchMode(), \r
-                                               configurator.getPageSize(), configurator.getPageNumber(), null, null);\r
-                       if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }\r
-                       if (names.size() > 0) {\r
-                               for (TaxonNameBase<?,?> taxonName : names) {\r
-                                       if (taxonName.getTaxonBases().size() == 0) {\r
-                                               results.add(taxonName);\r
-                                               numberNameResults++;\r
-                                       }\r
+               //remove synonym (if necessary)\r
+               if (synonym.getSynonymRelations().isEmpty()){\r
+                       TaxonNameBase<?,?> name = synonym.getName();\r
+                       synonym.setName(null);\r
+                       dao.delete(synonym);\r
+                       \r
+                       //remove name if possible (and required)\r
+                       if (name != null && removeNameIfPossible){\r
+                               try{\r
+                                       nameService.delete(name, new NameDeletionConfigurator());\r
+                               }catch (DataChangeNoRollbackException ex){\r
+                                       if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");\r
                                }\r
-                               if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }\r
-                               numberOfResults += numberNameResults;\r
                        }\r
                }\r
-               \r
-               // Taxa from common names\r
-               // FIXME the matching common names also must be returned\r
-               // FIXME implement search by area\r
-               if (configurator.isDoTaxaByCommonNames()) {\r
-                       int numberCommonNameResults = 0;\r
-                       List<CommonTaxonName> commonTaxonNames = \r
-                               descriptionDao.searchDescriptionByCommonName(configurator.getSearchString(), \r
-                                               configurator.getMatchMode(), configurator.getPageSize(), configurator.getPageNumber());\r
-                       if (logger.isDebugEnabled()) { logger.debug(commonTaxonNames.size() + " matching common name(s) found"); }\r
-                       if (commonTaxonNames.size() > 0) {\r
-                               for (CommonTaxonName commonTaxonName : commonTaxonNames) {\r
-                                       DescriptionBase description = commonTaxonName.getInDescription();\r
-                                       description = HibernateProxyHelper.deproxy(description, DescriptionBase.class);\r
-                                       if (description instanceof TaxonDescription) {\r
-                                               TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(description, TaxonDescription.class);\r
-                                               Taxon taxon = taxonDescription.getTaxon();\r
-                                               taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);\r
-                                               if (!results.contains(taxon) && !taxon.isMisappliedName()) {\r
-                                                       defaultBeanInitializer.initialize(taxon, configurator.getTaxonPropertyPath());\r
-                                                       results.add(taxon);\r
-                                                       numberCommonNameResults++;\r
-                                               }\r
-                                       } else {\r
-                                               logger.warn("Description of " + commonTaxonName.getName() + " is not an instance of TaxonDescription");\r
-                                       }\r
-                               }\r
-                               numberOfResults += numberCommonNameResults;\r
-                       } \r
-               }\r
-               \r
-               //FIXME does not work any more after model change\r
-               logger.warn("Sort does currently not work on identifiable entities due to model changes (duplicated implementation of the Comparable interface).");\r
-               //Collections.sort(results);\r
-               return new DefaultPagerImpl<IdentifiableEntity>\r
-                       (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);\r
-       }\r
-\r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * remove, clearly this method is never used\r
-        */\r
-       public <TYPE extends TaxonBase> Pager<TYPE> list(Class<TYPE> type,      Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,       List<String> propertyPaths) {\r
-               logger.warn("Pager<TYPE> list(xxx) method not yet implemented");\r
-               return null;\r
-       }\r
-\r
-       /*\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheOfAcceptedTaxa(eu.etaxonomy.cdm.model.taxon.TaxonomicTree)\r
-        \r
-       public List<UuidAndTitleCache> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByTaxonomicTree(TaxonomicTree taxonomicTree) {\r
-               return taxonDao.getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByTaxonomicTree(taxonomicTree);\r
        }\r
-       */\r
        \r
-       public Map<UUID, List<MediaRepresentation>> getAllMediaForChildNodes(Taxon taxon, TaxonomicTree taxTree, List<String> propertyPaths, int size, int height, int widthOrDuration, String[] mimeTypes){\r
-               TreeMap<UUID, List<MediaRepresentation>> result = new TreeMap<UUID, List<MediaRepresentation>>();\r
-               List<Media> taxonMedia = new ArrayList<Media>();\r
-               List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();\r
-               //add all media of the children to the result map\r
-               if (taxTree != null || taxon != null){\r
-                       \r
-                       taxTree = taxonTreeDao.load(taxTree.getUuid());\r
-                       //taxon = (Taxon)dao.load(taxon.getUuid());\r
-                                       \r
-                       List<TaxonNode> taxNodes = loadChildNodesOfTaxon(taxon, taxTree, propertyPaths);\r
-                       if (taxNodes.size() != 0){\r
-                       \r
-                       defaultBeanInitializer.initializeAll(taxNodes, propertyPaths);\r
-                       TaxonNode taxNode = loadTaxonNodeByTaxon(taxon, taxTree.getUuid(), propertyPaths);\r
-                       \r
-                       //Set<TaxonNode> childNodes = taxNode.getAllNodes();\r
-                       taxNodes.add(taxNode);\r
-                       if (taxNodes != null){\r
-                               for(TaxonNode childNode : taxNodes){\r
-                                       taxon = childNode.getTaxon();\r
-                                       /*Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
-                                       for (TaxonDescription taxDesc: descriptions){\r
-                                               Set<DescriptionElementBase> elements = taxDesc.getElements();\r
-                                               for (DescriptionElementBase descElem: elements){\r
-                                                       for(Media media : descElem.getMedia()){\r
-                                                               taxonMedia.add(media);\r
-                                                               \r
-                                                               //find the best matching representation\r
-                                                               medRep.add(media.findBestMatchingRepresentation(size, height, widthOrDuration, mimeTypes));\r
-                                                               \r
-                                                       }\r
-                                               }\r
-                                       }*/\r
-                                       result.put(taxon.getUuid(), getAllMedia(taxon, size, height, widthOrDuration,mimeTypes));\r
-                                                                               \r
-                               }       \r
-                       }\r
-                       }\r
-                       \r
-               }\r
-               \r
-               \r
-               return result;\r
-       }\r
-\r
+       \r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)\r
+     */\r
+    public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {\r
 \r
-       public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){\r
-               List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();\r
-               taxon = (Taxon)dao.load(taxon.getUuid());\r
-               Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
-               for (TaxonDescription taxDesc: descriptions){\r
-                       Set<DescriptionElementBase> elements = taxDesc.getElements();\r
-                       for (DescriptionElementBase descElem: elements){\r
-                               for(Media media : descElem.getMedia()){\r
-                                                                       \r
-                                       //find the best matching representation\r
-                                       medRep.add(media.findBestMatchingRepresentation(size, height, widthOrDuration, mimeTypes));\r
-                                       \r
-                               }\r
-                       }\r
-               }\r
-               return medRep;\r
-       }\r
+        return this.dao.findIdenticalNamesNew(propertyPath);\r
+    }\r
 \r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+     */\r
+    public String getPhylumName(TaxonNameBase name){\r
+        return this.dao.getPhylumName(name);\r
+    }\r
 \r
-       \r
-       \r
-       \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)\r
+        */\r
+       public long deleteSynonymRelationships(Synonym syn) {\r
+               return dao.deleteSynonymRelationships(syn, null);\r
+       }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listSynonymRelationships(eu.etaxonomy.cdm.model.taxon.TaxonBase, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List, eu.etaxonomy.cdm.model.common.RelationshipBase.Direction)\r
+     */\r
+    public List<SynonymRelationship> listSynonymRelationships(\r
+            TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,\r
+            List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {\r
+        Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);\r
+\r
+        List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
+        if(numberOfResults > 0) { // no point checking again\r
+            results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);\r
+        }\r
+        return results;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)\r
+     */\r
+    @Override\r
+    public Taxon findBestMatchingTaxon(String taxonName) {\r
+        MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();\r
+        config.setTaxonNameTitle(taxonName);\r
+        return findBestMatchingTaxon(config);\r
+    }\r
+\r
+\r
+\r
+    @Override\r
+    public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {\r
+\r
+        Taxon bestCandidate = null;\r
+        try{\r
+            // 1. search for acceptet taxa\r
+            List<TaxonBase> taxonList = dao.findByNameTitleCache(Taxon.class, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);\r
+            boolean bestCandidateMatchesSecUuid = false;\r
+            boolean bestCandidateIsInClassification = false;\r
+            int countEqualCandidates = 0;\r
+            for(TaxonBase taxonBaseCandidate : taxonList){\r
+                if(taxonBaseCandidate instanceof Taxon){\r
+                    Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);\r
+                    boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);\r
+                    if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){\r
+                        continue;\r
+                    }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){\r
+                        bestCandidate = newCanditate;\r
+                        countEqualCandidates = 1;\r
+                        bestCandidateMatchesSecUuid = true;\r
+                        continue;\r
+                    }\r
+\r
+                    boolean newCandidateInClassification = isInClassification(newCanditate, config);\r
+                    if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){\r
+                        continue;\r
+                    }else if (newCandidateInClassification && ! bestCandidateIsInClassification){\r
+                        bestCandidate = newCanditate;\r
+                        countEqualCandidates = 1;\r
+                        bestCandidateIsInClassification = true;\r
+                        continue;\r
+                    }\r
+                    if (bestCandidate == null){\r
+                        bestCandidate = newCanditate;\r
+                        countEqualCandidates = 1;\r
+                        continue;\r
+                    }\r
+\r
+                }else{  //not Taxon.class\r
+                    continue;\r
+                }\r
+                countEqualCandidates++;\r
+\r
+            }\r
+            if (bestCandidate != null){\r
+                if(countEqualCandidates > 1){\r
+                    logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());\r
+                    return bestCandidate;\r
+                } else {\r
+                    logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());\r
+                    return bestCandidate;\r
+                }\r
+            }\r
+\r
+\r
+            // 2. search for synonyms\r
+            if (config.isIncludeSynonyms()){\r
+                List<TaxonBase> synonymList = dao.findByNameTitleCache(Synonym.class, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);\r
+                for(TaxonBase taxonBase : synonymList){\r
+                    if(taxonBase instanceof Synonym){\r
+                        Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);\r
+                        Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();\r
+                        if(!acceptetdCandidates.isEmpty()){\r
+                            bestCandidate = acceptetdCandidates.iterator().next();\r
+                            if(acceptetdCandidates.size() == 1){\r
+                                logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());\r
+                                return bestCandidate;\r
+                            } else {\r
+                                logger.info("using accepted Taxon " +  bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());\r
+                                return bestCandidate;\r
+                            }\r
+                            //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+        } catch (Exception e){\r
+            logger.error(e);\r
+        }\r
+\r
+        return bestCandidate;\r
+    }\r
+\r
+    private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {\r
+        UUID configClassificationUuid = config.getClassificationUuid();\r
+        if (configClassificationUuid == null){\r
+            return false;\r
+        }\r
+        for (TaxonNode node : taxon.getTaxonNodes()){\r
+            UUID classUuid = node.getClassification().getUuid();\r
+            if (configClassificationUuid.equals(classUuid)){\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {\r
+        UUID configSecUuid = config.getSecUuid();\r
+        if (configSecUuid == null){\r
+            return false;\r
+        }\r
+        UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();\r
+        return configSecUuid.equals(taxonSecUuid);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)\r
+     */\r
+    @Override\r
+    public Synonym findBestMatchingSynonym(String taxonName) {\r
+        List<TaxonBase> synonymList = dao.findByNameTitleCache(Synonym.class, taxonName, null, MatchMode.EXACT, null, 0, null, null);\r
+        if(! synonymList.isEmpty()){\r
+            Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);\r
+            if(synonymList.size() == 1){\r
+                logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );\r
+                return result;\r
+            } else {\r
+                logger.info("Several matching synonyms found. Using first: " +  result.getTitleCache());\r
+                return result;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#moveSynonymToAnotherTaxon(eu.etaxonomy.cdm.model.taxon.SynonymRelationship, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String, boolean)\r
+     */\r
+    @Override\r
+    public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,\r
+               SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {\r
+        \r
+       Synonym synonym = oldSynonymRelation.getSynonym();\r
+       Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();\r
+        //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)\r
+       TaxonNameBase<?,?> synonymName = synonym.getName();\r
+        TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();\r
+        //set default relationship type\r
+        if (newSynonymRelationshipType == null){\r
+               newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+        }\r
+        boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());\r
+        \r
+        HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();\r
+        int hgSize = homotypicGroup.getTypifiedNames().size();\r
+        boolean isSingleInGroup = !(hgSize > 1);\r
+        \r
+        if (! isSingleInGroup){\r
+               boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);\r
+            boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;\r
+               if (isHomotypicToAccepted){\r
+               String message = "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";\r
+               String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";\r
+               message = String.format(message, homotypicRelatives);\r
+               throw new HomotypicalGroupChangeException(message);\r
+            }\r
+               if (! moveHomotypicGroup){\r
+                       String message = "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";\r
+               throw new HomotypicalGroupChangeException(message);\r
+            }\r
+        }else{\r
+               moveHomotypicGroup = true;  //single synonym always allows to moveCompleteGroup\r
+        }\r
+        Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);\r
+        \r
+        SynonymRelationship result = null;\r
+        //move all synonyms to new taxon\r
+        List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);\r
+        for (Synonym syn: homotypicSynonyms){\r
+               Set<SynonymRelationship> synRelations = syn.getSynonymRelations();\r
+               for (SynonymRelationship synRelation : synRelations){\r
+                       if (fromTaxon.equals(synRelation.getAcceptedTaxon())){\r
+                               Reference<?> newReference = reference;\r
+                               if (newReference == null && keepReference){\r
+                                       newReference = synRelation.getCitation();\r
+                               }\r
+                               String newRefDetail = referenceDetail;\r
+                               if (newRefDetail == null && keepReference){\r
+                                       newRefDetail = synRelation.getCitationMicroReference();\r
+                               }\r
+                               SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);\r
+                       fromTaxon.removeSynonymRelation(synRelation, false);\r
+//                     \r
+                       //change homotypic group of synonym if relType is 'homotypic'\r
+//                     if (newRelTypeIsHomotypic){\r
+//                             newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());\r
+//                     }\r
+                       //set result\r
+                       if (synRelation.equals(oldSynonymRelation)){\r
+                               result = newSynRelation;\r
+                       }\r
+                       }\r
+               }\r
+               \r
+        }\r
+        saveOrUpdate(newTaxon);\r
+        //Assert that there is a result \r
+        if (result == null){\r
+               String message = "Old synonym relation could not be transformed into new relation. This should not happen.";\r
+               throw new IllegalStateException(message);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()\r
+     */\r
+    @Override\r
+    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {\r
+        return dao.getUuidAndTitleCacheTaxon();\r
+    }\r
 \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()\r
+        */\r
+       @Override\r
+       public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {\r
+               return dao.getUuidAndTitleCacheSynonym();\r
+       }\r
 \r
 }\r