fix createInferredSynonyms test
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index d630fdb8156a731e3f6859f2baea05306a1ab295..8346910644a7991e511dac8608a03d9c409ed61f 100644 (file)
@@ -1,47 +1,67 @@
 // $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
 \r
 package eu.etaxonomy.cdm.api.service;\r
 \r
+import java.io.IOException;\r
 import java.util.ArrayList;\r
-import java.util.Comparator;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
-import java.util.Iterator;\r
 import java.util.List;\r
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
 import org.apache.log4j.Logger;\r
+import org.apache.lucene.index.CorruptIndexException;\r
+import org.apache.lucene.queryParser.ParseException;\r
+import org.apache.lucene.search.TopDocs;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Service;\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.config.TaxonDeletionConfigurator;\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.exception.ReferencedObjectUndeletableException;\r
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;\r
+import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
+import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
+import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
-import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
+import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.IIdentificationKey;\r
+import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.description.TaxonInteraction;\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.name.ZoologicalName;\r
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;\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
@@ -50,37 +70,52 @@ 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.ICdmGenericDao;\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.fetch.CdmFetch;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
 import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;\r
+import eu.etaxonomy.cdm.search.LuceneSearch;\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(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
-       \r
+       public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";\r
+\r
+       public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";\r
+\r
+       public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";\r
+\r
+\r
+    @Autowired\r
+    private ITaxonNameDao nameDao;\r
+\r
+    @Autowired\r
+    private ISearchResultBuilder searchResultBuilder;\r
 \r
        @Autowired\r
-       private IOrderedTermVocabularyDao orderedVocabularyDao;\r
-       @Autowired\r
-       private IDescriptionDao descriptionDao;\r
+       private INameService nameService;\r
+\r
        @Autowired\r
-       private BeanInitializer defaultBeanInitializer;\r
-       \r
-       private Comparator<? super TaxonNode> taxonNodeComparator;\r
+       private ICdmGenericDao genericDao;\r
+\r
        @Autowired\r
-       public void setTaxonNodeComparator(ITaxonNodeComparator<? super TaxonNode> taxonNodeComparator){\r
-               this.taxonNodeComparator = (Comparator<? super TaxonNode>) taxonNodeComparator;\r
-       }\r
+       private IDescriptionService descriptionService;\r
+\r
+    @Autowired\r
+    private IOrderedTermVocabularyDao orderedVocabularyDao;\r
        \r
        /**\r
         * Constructor\r
@@ -89,498 +124,1573 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
                if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }\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
+     * 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#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)\r
+     */\r
+    @Transactional(readOnly = false)\r
+    public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
+\r
+        TaxonNameBase<?,?> synonymName = synonym.getName();\r
+        synonymName.removeTaxonBase(synonym);\r
+        TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();\r
+        taxonName.removeTaxonBase(acceptedTaxon);\r
+\r
+        synonym.setName(taxonName);\r
+        acceptedTaxon.setName(synonymName);\r
+\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
+    /* (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
+    //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
+        TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();\r
+        TaxonNameBase<?,?> synonymName = synonym.getName();\r
+        HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();\r
+\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
+        Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());\r
+\r
+        SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+        List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);\r
+\r
+        for (Synonym heteroSynonym : heteroSynonyms){\r
+            if (synonym.equals(heteroSynonym)){\r
+                acceptedTaxon.removeSynonym(heteroSynonym, false);\r
+            }else{\r
+                //move synonyms in same homotypic group to new accepted taxon\r
+                heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);\r
+            }\r
+        }\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
+        }\r
+\r
+        return newAcceptedTaxon;\r
+    }\r
+\r
+\r
+    public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){\r
+\r
+        // Get name from synonym\r
+        TaxonNameBase<?, ?> synonymName = synonym.getName();\r
+\r
+        // remove synonym from taxon\r
+        toTaxon.removeSynonym(synonym);\r
+\r
+        // Create a taxon with synonym name\r
+        Taxon fromTaxon = Taxon.NewInstance(synonymName, null);\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
+        return fromTaxon;\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
+        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
+    /* (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 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 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
+    /* (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(synonym, 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#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> 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 List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){\r
+\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
+\r
+        result = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
+        return result;\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
+\r
+        List<String> propertyPath = new ArrayList<String>();\r
+        if(configurator.getTaxonPropertyPath() != null){\r
+            propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+        }\r
+\r
+\r
+       if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){\r
+            if(configurator.getPageSize() != null){ // no point counting if we need all anyway\r
+                numberTaxaResults =\r
+                    dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),\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(configurator.isDoTaxa(), configurator.isDoSynonyms(),\r
+                    configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),\r
+                    configurator.getMatchMode(), configurator.getNamedAreas(),\r
+                    configurator.getPageSize(), 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 = new ArrayList<TaxonBase>();\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
+                List<Object[]> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());\r
+                for( Object[] entry : commonNameResults ) {\r
+                    taxa.add((TaxonBase) entry[0]);\r
+                }\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
+   \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#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)\r
+     */\r
+    @Override\r
+    public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config) throws ReferencedObjectUndeletableException {\r
+       if (config == null){\r
+               config = new TaxonDeletionConfigurator();\r
+       }\r
+       \r
+                       //      TaxonNode\r
+                       if (! config.isDeleteTaxonNodes()){\r
+                               if (taxon.getTaxonNodes().size() > 0){\r
+                                       String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";\r
+                                       throw new ReferencedObjectUndeletableException(message);\r
+                               }\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
+                       //      SynonymRelationShip\r
+                       if (config.isDeleteSynonymRelations()){\r
+                               boolean removeSynonymNameFromHomotypicalGroup = false;\r
+                               for (SynonymRelationship synRel : taxon.getSynonymRelations()){\r
+                                       Synonym synonym = synRel.getSynonym();\r
+                                       taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);\r
+                                       if (config.isDeleteSynonymsIfPossible()){\r
+                                               //TODO which value\r
+                                               boolean newHomotypicGroupIfNeeded = true;\r
+                                               deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);\r
+                                       }else{\r
+                                               deleteSynonymRelationships(synonym, taxon);\r
+                                       }\r
+                               }\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
+                       //      TaxonRelationship       \r
+                       if (! config.isDeleteTaxonRelationships()){\r
+                               if (taxon.getTaxonRelations().size() > 0){\r
+                                       String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";\r
+                                       throw new ReferencedObjectUndeletableException(message);\r
+                               }\r
+                       }\r
+       \r
+                       \r
+                       //      TaxonDescription\r
+                               Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
+                               \r
+                               for (TaxonDescription desc: descriptions){\r
+                                       if (config.isDeleteDescriptions()){\r
+                                               //TODO use description delete configurator ?\r
+                                               //FIXME check if description is ALWAYS deletable\r
+                                               descriptionService.delete(desc); \r
+                                       }else{\r
+                                               if (desc.getDescribedSpecimenOrObservations().size()>0){\r
+                                                       String message = "Taxon can't be deleted as it is used in a TaxonDescription" +\r
+                                                                       " which also describes specimens or abservations";\r
+                                                       throw new ReferencedObjectUndeletableException(message);\r
+                                               }\r
+                                       }\r
+                               }\r
+        \r
+                       \r
+                       //check references with only reverse mapping\r
+                       Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);\r
+                       for (CdmBase referencingObject : referencingObjects){\r
+                               //IIdentificationKeys (Media, Polytomous, MultiAccess)\r
+                               if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){\r
+                                       String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+                                       message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnitBase.class).getTitleCache());\r
+                                       throw new ReferencedObjectUndeletableException(message);\r
+                               }\r
+       \r
+                               \r
+                               //PolytomousKeyNode\r
+                               if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+                                       String message = "Taxon can't be deleted as it is used in polytomous key node";\r
+                                       throw new ReferencedObjectUndeletableException(message);\r
+                               }\r
+                               \r
+                               //TaxonInteraction\r
+                               if (referencingObject.isInstanceOf(TaxonInteraction.class)){\r
+                                       String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+                                       throw new ReferencedObjectUndeletableException(message);\r
+                               }\r
+                       }\r
+                       \r
+                       \r
+                       //TaxonNameBase\r
+                       if (config.isDeleteNameIfPossible()){\r
+                       try {\r
+                                       nameService.delete(taxon.getName(), config.getNameDeletionConfig());\r
+                               } catch (ReferencedObjectUndeletableException e) {\r
+                                       //do nothing\r
+                                       if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}\r
+                               }\r
+                       }\r
 \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
+    /* (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
+        synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\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
+        //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
+        for (Taxon relatedTaxon : taxonSet){\r
+//                     dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
+            relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);\r
+        }\r
+        this.saveOrUpdate(synonym);\r
+\r
+        //TODO remove name from homotypical group?\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
+            }\r
+        }\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
+        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
        /* (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
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\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
+       public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {\r
+               return dao.deleteSynonymRelationships(syn, taxon);\r
        }\r
 \r
-       public List<RelationshipBase> getAllRelationships(int limit, int start){\r
-               return dao.getAllRelationships(limit, start);\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(true, false, 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(false, true, 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(false, true, 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
+    @Override\r
+    public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(Class<? extends DescriptionElementBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,\r
+            List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+        String luceneQueryTemplate = "titleCache:%1$s OR multilanguageText.text:%1$s OR name:%1$s";\r
+        String luceneQuery = String.format(luceneQueryTemplate, queryString);\r
+\r
+        LuceneSearch luceneSearch = new LuceneSearch(getSession(), clazz);\r
+        TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQuery);\r
+        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id");\r
+\r
+        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);\r
+\r
+    }\r
+    \r
+    public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){\r
+        List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
+        List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();\r
+\r
+        HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();\r
+        \r
+        \r
+        UUID uuid= taxon.getName().getUuid();\r
+        ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);\r
+        String epithetOfTaxon = null;\r
+        String infragenericEpithetOfTaxon = null;\r
+        String infraspecificEpithetOfTaxon = null;\r
+        if (taxonName.isSpecies()){\r
+                epithetOfTaxon= taxonName.getSpecificEpithet();\r
+        } else if (taxonName.isInfraGeneric()){\r
+               infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();\r
+        } else if (taxonName.isInfraSpecific()){\r
+               infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();\r
+        }\r
+        String genusOfTaxon = taxonName.getGenusOrUninomial();\r
+        Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
+         List<String> taxonNames = new ArrayList<String>();\r
+\r
+        for (TaxonNode node: nodes){\r
+           // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName\r
+           // List<String> synonymsEpithet = new ArrayList<String>();\r
+\r
+            if (node.getClassification().equals(classification)){\r
+                if (!node.isTopmostNode()){\r
+                       TaxonNode parent = (TaxonNode)node.getParent();\r
+                       parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);\r
+                       TaxonNameBase parentName =  parent.getTaxon().getName();\r
+                       ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);\r
+                       Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());\r
+                       Rank rankOfTaxon = taxonName.getRank();\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
+                       //create inferred synonyms for species, subspecies\r
+                       if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){\r
+       \r
+                           Synonym inferredEpithet = null;\r
+                           Synonym inferredGenus = null;\r
+                           Synonym potentialCombination = null;\r
+       \r
+                           List<String> propertyPaths = new ArrayList<String>();\r
+                           propertyPaths.add("synonym");\r
+                           propertyPaths.add("synonym.name");\r
+                           List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
+                           orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));\r
+       \r
+                           List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
+                           List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
+                           \r
+                           List<TaxonRelationship> taxonRelListParent = null;\r
+                           List<TaxonRelationship> taxonRelListTaxon = null;\r
+                           if (doWithMisappliedNames){\r
+                               taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
+                               taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
+                           }\r
+                           \r
+       \r
+                           if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){\r
+                               Set<String> genusNames = new HashSet<String>();\r
+                                                               \r
+                               for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
+                                   Synonym syn = synonymRelationOfParent.getSynonym();\r
+                                   \r
+                                   inferredEpithet = createInferredEpithets(taxon, \r
+                                                                               zooHashMap, taxonName, epithetOfTaxon,\r
+                                                                               infragenericEpithetOfTaxon,\r
+                                                                               infraspecificEpithetOfTaxon,\r
+                                                                               taxonNames, parentName, \r
+                                                                               syn);\r
+                                   \r
+                                   \r
+                                   inferredSynonyms.add(inferredEpithet);\r
+                                   zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
+                                       taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
+                               }\r
+                               \r
+                               if (doWithMisappliedNames){\r
+                                       \r
+                                       for (TaxonRelationship taxonRelationship: taxonRelListParent){\r
+                                                Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+                                               \r
+                                                inferredEpithet = createInferredEpithets(taxon,\r
+                                                                                       zooHashMap, taxonName, epithetOfTaxon,\r
+                                                                                       infragenericEpithetOfTaxon,\r
+                                                                                       infraspecificEpithetOfTaxon,\r
+                                                                                       taxonNames, parentName, \r
+                                                                                       misappliedName);\r
+                                               \r
+                                               inferredSynonyms.add(inferredEpithet);\r
+                                               zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
+                                               taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
+                                       }\r
+                               }\r
+       \r
+                               if (!taxonNames.isEmpty()){\r
+                               List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+                               ZoologicalName name;\r
+                               if (!synNotInCDM.isEmpty()){\r
+                                   inferredSynonymsToBeRemoved.clear();\r
+       \r
+                                   for (Synonym syn :inferredSynonyms){\r
+                                       name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+                                       if (!synNotInCDM.contains(name.getNameCache())){\r
+                                           inferredSynonymsToBeRemoved.add(syn);\r
+                                       }\r
+                                   }\r
+       \r
+                                   // Remove identified Synonyms from inferredSynonyms\r
+                                   for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+                                       inferredSynonyms.remove(synonym);\r
+                                   }\r
+                               }\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 changeAcceptedTaxonToSynonym(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, ReferenceBase citation, String citationMicroReference) {\r
+                    }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){\r
 \r
-               // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon\r
-               // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that\r
-               if (oldTaxonNode == null || newAcceptedTaxonNode == null || oldTaxonNode.getTaxon().getName() == null){\r
-                       throw new IllegalArgumentException("A mandatory parameter was null.");\r
-               }\r
+\r
+                        for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
+                            TaxonNameBase synName;\r
+                            ZoologicalName inferredSynName;\r
+\r
+                            Synonym syn = synonymRelationOfTaxon.getSynonym();\r
+                            inferredGenus = createInferredGenus(taxon,\r
+                                                                       zooHashMap, taxonName, epithetOfTaxon,\r
+                                                                       genusOfTaxon, taxonNames, zooParentName, syn);\r
+                            \r
+                            inferredSynonyms.add(inferredGenus);\r
+                            zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
+                               taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
+                            \r
+                            \r
+                        }\r
+                        \r
+                        if (doWithMisappliedNames){\r
+                               \r
+                               for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
+                                       Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+                                       inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);\r
+                                       \r
+                                       inferredSynonyms.add(inferredGenus);\r
+                                       zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
+                                       taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
+                               }\r
+                        }\r
+                        \r
+\r
+                        if (!taxonNames.isEmpty()){\r
+                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+                            ZoologicalName name;\r
+                            if (!synNotInCDM.isEmpty()){\r
+                                inferredSynonymsToBeRemoved.clear();\r
+\r
+                                for (Synonym syn :inferredSynonyms){\r
+                                    name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+                                    if (!synNotInCDM.contains(name.getNameCache())){\r
+                                        inferredSynonymsToBeRemoved.add(syn);\r
+                                    }\r
+                                }\r
+\r
+                                // Remove identified Synonyms from inferredSynonyms\r
+                                for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+                                    inferredSynonyms.remove(synonym);\r
+                                }\r
+                            }\r
+                        }\r
+\r
+                    }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){\r
+\r
+                        Reference sourceReference = null; // TODO: Determination of sourceReference is redundant\r
+                        ZoologicalName inferredSynName;\r
+                        //for all synonyms of the parent...\r
+                        for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
+                            TaxonNameBase synName;\r
+                            Synonym synParent = synonymRelationOfParent.getSynonym();\r
+                            synName = synParent.getName();\r
+\r
+                            HibernateProxyHelper.deproxy(synParent);\r
+\r
+                            // Set the sourceReference\r
+                            sourceReference = synParent.getSec();\r
+\r
+                            // Determine the idInSource\r
+                            String idInSourceParent = getIdInSource(synParent);\r
+\r
+                            ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+                            String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
+                            String synParentInfragenericName = null;\r
+                            String synParentSpecificEpithet = null;\r
+                            \r
+                            if (parentSynZooName.isInfraGeneric()){\r
+                               synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
+                            }\r
+                            if (parentSynZooName.isSpecies()){\r
+                               synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
+                            }\r
+                            \r
+                           /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
+                                synonymsGenus.put(synGenusName, idInSource);\r
+                            }*/\r
+                            \r
+                            //for all synonyms of the taxon\r
+                            \r
+                            for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
+                               \r
+                                Synonym syn = synonymRelationOfTaxon.getSynonym();\r
+                                ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+                                potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,\r
+                                                                               synParentGenus,\r
+                                                                               synParentInfragenericName,\r
+                                                                               synParentSpecificEpithet, syn, zooHashMap);\r
+                                \r
+                                taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
+                                inferredSynonyms.add(potentialCombination);\r
+                                zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
+                                       taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
+                                \r
+                            }\r
+                            \r
+                            \r
+                        }\r
+                        \r
+                        if (doWithMisappliedNames){\r
+                               \r
+                               for (TaxonRelationship parentRelationship: taxonRelListParent){\r
+                               \r
+                                       TaxonNameBase misappliedParentName;\r
+                                       \r
+                                Taxon misappliedParent = parentRelationship.getFromTaxon();\r
+                                misappliedParentName = misappliedParent.getName();\r
+\r
+                                HibernateProxyHelper.deproxy(misappliedParent);\r
+\r
+                                // Set the sourceReference\r
+                                sourceReference = misappliedParent.getSec();\r
+\r
+                                // Determine the idInSource\r
+                                String idInSourceParent = getIdInSource(misappliedParent);\r
+\r
+                                ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);\r
+                                String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
+                                String synParentInfragenericName = null;\r
+                                String synParentSpecificEpithet = null;\r
+                                \r
+                                if (parentSynZooName.isInfraGeneric()){\r
+                                       synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
+                                }\r
+                                if (parentSynZooName.isSpecies()){\r
+                                       synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
+                                }\r
+                                \r
+                                       \r
+                                       for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
+                                               Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+                                               ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);\r
+                                               potentialCombination = createPotentialCombination(\r
+                                                                                       idInSourceParent, parentSynZooName, zooMisappliedName,\r
+                                                                                       synParentGenus,\r
+                                                                                       synParentInfragenericName,\r
+                                                                                       synParentSpecificEpithet, misappliedName, zooHashMap);\r
+                                               \r
+                                               \r
+                                               taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
+                                               inferredSynonyms.add(potentialCombination);\r
+                                               zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
+                                               taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
+                                       }\r
+                               }\r
+                        }\r
+                        \r
+                        if (!taxonNames.isEmpty()){\r
+                               List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+                            ZoologicalName name;\r
+                            if (!synNotInCDM.isEmpty()){\r
+                               inferredSynonymsToBeRemoved.clear();\r
+                               for (Synonym syn :inferredSynonyms){\r
+                                       try{\r
+                                               name = (ZoologicalName) syn.getName();\r
+                                    }catch (ClassCastException e){\r
+                                       name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+                                    }\r
+                                    if (!synNotInCDM.contains(name.getNameCache())){\r
+                                       inferredSynonymsToBeRemoved.add(syn);\r
+                                    }\r
+                                 }\r
+                               // Remove identified Synonyms from inferredSynonyms\r
+                                for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+                                       inferredSynonyms.remove(synonym);\r
+                                }\r
+                            }\r
+                         }\r
+                        }\r
+                    }else {\r
+                        logger.info("The synonymrelationship type is not defined.");\r
+                        return inferredSynonyms;\r
+                    }\r
+                }\r
+            }\r
+          \r
+        }\r
+\r
+        return inferredSynonyms;\r
+    }\r
+\r
+       private Synonym createPotentialCombination(String idInSourceParent,\r
+                       ZoologicalName parentSynZooName,        ZoologicalName zooSynName, String synParentGenus,\r
+                       String synParentInfragenericName, String synParentSpecificEpithet,\r
+                       TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {\r
+               Synonym potentialCombination;\r
+               Reference sourceReference;\r
+               ZoologicalName inferredSynName;\r
+               HibernateProxyHelper.deproxy(syn);\r
+\r
+               // Set sourceReference\r
+               sourceReference = syn.getSec();\r
+\r
+       \r
+               String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();\r
+                       \r
+               String synTaxonInfraSpecificName= null;\r
                \r
-               if(oldTaxonNode.equals(newAcceptedTaxonNode)){\r
-                       throw new IllegalArgumentException("Taxon can not be made synonym of its own.");\r
+               if (parentSynZooName.isSpecies()){\r
+                       synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();\r
                }\r
                \r
-               Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());\r
-               Taxon newAcceptedTaxon = (Taxon) HibernateProxyHelper.deproxy(newAcceptedTaxonNode.getTaxon());\r
+               /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){\r
+                   synonymsEpithet.add(epithetName);\r
+               }*/\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
-                       }else{\r
-                               synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
-                       }\r
-               }\r
-               SynonymRelationship synonmyRelationship = newAcceptedTaxon.addSynonymName(synonymName, synonymRelationshipType, citation, citationMicroReference);\r
+               //create potential combinations...\r
+               inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());\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
+               inferredSynName.setGenusOrUninomial(synParentGenus);\r
+               if (zooSynName.isSpecies()){\r
+                         inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);\r
+                         if (parentSynZooName.isInfraGeneric()){\r
+                                 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
+                         }\r
                }\r
-\r
-               \r
-               // CHILD NODES\r
-               if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){\r
-                       for(TaxonNode childNode : oldTaxonNode.getChildNodes()){\r
-                               newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference(), childNode.getSynonymToBeUsed());\r
-                       }\r
+               if (zooSynName.isInfraSpecific()){\r
+                       inferredSynName.setSpecificEpithet(synParentSpecificEpithet);\r
+                       inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);\r
                }\r
-               \r
-               //Move Taxon RelationShips to new Taxon\r
-               Set<TaxonRelationship> obsoleteTaxonRelationships = new HashSet<TaxonRelationship>();\r
-               for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){\r
-                       Taxon fromTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());\r
-                       Taxon toTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());\r
-                       if (fromTaxon == oldTaxon){\r
-                               newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(), \r
-                                               taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());\r
-                               \r
-                       }else if(toTaxon == oldTaxon){\r
-                               taxonRelationship.getFromTaxon().addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(), \r
-                                               taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());\r
-\r
-                       }else{\r
-                               logger.warn("Taxon is not part of its own Taxonrelationship");\r
-                       }\r
-                       // Remove old relationships\r
-                       taxonRelationship.setToTaxon(null);\r
-                       taxonRelationship.setFromTaxon(null);\r
+               if (parentSynZooName.isInfraGeneric()){\r
+                       inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
                }\r
                \r
-               //Move descriptions to new taxon\r
-               for(TaxonDescription description : oldTaxon.getDescriptions()){\r
-                       description.setTitleCache("Description copied from former accepted taxon: " + oldTaxon.getTitleCache() + "(Old title: " + description.getTitleCache()  + ")");\r
-                       newAcceptedTaxon.addDescription(description);\r
-               }\r
-                               \r
-               oldTaxonNode.delete();\r
-               \r
-               return synonmyRelationship.getSynonym();\r
-       }\r
+                        \r
+               potentialCombination = Synonym.NewInstance(inferredSynName, null);\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 swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
-               \r
-               TaxonNameBase synonymName = synonym.getName();\r
-               synonymName.removeTaxonBase(synonym);\r
-               TaxonNameBase taxonName = acceptedTaxon.getName();\r
-               taxonName.removeTaxonBase(acceptedTaxon);\r
-               \r
-               synonym.setName(taxonName);\r
-               acceptedTaxon.setName(synonymName);\r
+               // Set the sourceReference\r
+               potentialCombination.setSec(sourceReference);\r
+\r
+                        \r
+               // Determine the idInSource\r
+               String idInSourceSyn= getIdInSource(syn);\r
                \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
+               if (idInSourceParent != null && idInSourceSyn != null) {\r
+                   IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+                   inferredSynName.addSource(originalSource);\r
+                   originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+                   potentialCombination.addSource(originalSource);\r
+               }\r
                \r
+               inferredSynName.generateTitle();\r
        \r
-       /*\r
-        * (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeSynonymAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\r
-        */\r
-       public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
-               \r
-               Taxon newAcceptedTaxon = Taxon.NewInstance(synonym.getName(), acceptedTaxon.getSec());\r
-               \r
-               acceptedTaxon.removeSynonym(synonym);\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
-               return newAcceptedTaxon;\r
+               return potentialCombination;\r
        }\r
-       \r
-       public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, ReferenceBase citation, String microcitation){\r
-               \r
-               // Get name from synonym\r
-               TaxonNameBase<?, ?> synonymName = synonym.getName();\r
-               \r
-               // remove synonym from taxon\r
-               toTaxon.removeSynonym(synonym);\r
-               \r
-               // Create a taxon with synonym name\r
-               Taxon fromTaxon = Taxon.NewInstance(synonymName, null);\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
+       private Synonym createInferredGenus(Taxon taxon,\r
+                       HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
+                       String epithetOfTaxon, String genusOfTaxon,\r
+                       List<String> taxonNames, ZoologicalName zooParentName,\r
+                       TaxonBase syn) {\r
                \r
-               return fromTaxon;\r
-       }\r
+               Synonym inferredGenus;\r
+               TaxonNameBase synName;\r
+               ZoologicalName inferredSynName;\r
+               synName =syn.getName();\r
+               HibernateProxyHelper.deproxy(syn);\r
 \r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache()\r
-        */\r
-       @Override\r
-       @Transactional(readOnly = false)\r
-       public void updateTitleCache() {\r
-               Class<TaxonBase> clazz = TaxonBase.class;\r
-               super.updateTitleCache(clazz, null, null);\r
-       }\r
-       \r
-       @Autowired\r
-       protected void setDao(ITaxonDao dao) {\r
-               this.dao = dao;\r
-       }\r
+               // Determine the idInSource\r
+               String idInSourceSyn = getIdInSource(syn);\r
+               String idInSourceTaxon = getIdInSource(taxon);\r
+               // Determine the sourceReference\r
+               Reference sourceReference = syn.getSec();\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
-               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
+               synName = syn.getName();\r
+               ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+               String synSpeciesEpithetName = synZooName.getSpecificEpithet();\r
+                     /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){\r
+                   synonymsEpithet.add(synSpeciesEpithetName);\r
+               }*/\r
+                     \r
+               inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
+               //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...\r
+\r
+\r
+               inferredSynName.setGenusOrUninomial(genusOfTaxon);\r
+               if (zooParentName.isInfraGeneric()){\r
+                       inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());\r
                }\r
                \r
-               return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
+               if (taxonName.isSpecies()){\r
+                       inferredSynName.setSpecificEpithet(synSpeciesEpithetName);\r
+               }\r
+               if (taxonName.isInfraSpecific()){\r
+                       inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
+                       inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());\r
+               }\r
+                     \r
+                   \r
+               inferredGenus = Synonym.NewInstance(inferredSynName, null);\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
+               // Set the sourceReference\r
+               inferredGenus.setSec(sourceReference);\r
+\r
+               // Add the original source\r
+               if (idInSourceSyn != null && idInSourceTaxon != null) {\r
+                   IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+                   inferredGenus.addSource(originalSource);\r
+                   \r
+                   originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+                   inferredSynName.addSource(originalSource);\r
+                   \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
-        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
+\r
+               taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());\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
+               inferredSynName.generateTitle();\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
-        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
+               return inferredGenus;\r
        }\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
+       private Synonym createInferredEpithets(Taxon taxon,\r
+                       HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
+                       String epithetOfTaxon, String infragenericEpithetOfTaxon,\r
+                       String infraspecificEpithetOfTaxon, List<String> taxonNames,\r
+                       TaxonNameBase parentName, TaxonBase syn) {\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
+               Synonym inferredEpithet;\r
+               TaxonNameBase synName;\r
+               ZoologicalName inferredSynName;\r
+               HibernateProxyHelper.deproxy(syn);\r
+\r
+               // Determine the idInSource\r
+               String idInSourceSyn = getIdInSource(syn);\r
+               String idInSourceTaxon =  getIdInSource(taxon);\r
+               // Determine the sourceReference\r
+               Reference sourceReference = syn.getSec();\r
+\r
+               synName = syn.getName();\r
+               ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+               String synGenusName = zooSynName.getGenusOrUninomial();\r
+               String synInfraGenericEpithet = null;\r
+               String synSpecificEpithet = null;\r
                \r
-               return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-       }\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
+               if (zooSynName.getInfraGenericEpithet() != null){\r
+                       synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();\r
+               } \r
                \r
-               List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
-               if(numberOfResults > 0) { // no point checking again\r
-                       results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths); \r
-               }\r
+               if (zooSynName.isInfraSpecific()){\r
+                       synSpecificEpithet = zooSynName.getSpecificEpithet();\r
+               } \r
                \r
-               return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-       }\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
-       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
-               }\r
-               return heterotypicSynonymyGroups;\r
-       }\r
-       \r
-       public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {\r
+                     /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
+                   synonymsGenus.put(synGenusName, idInSource);\r
+               }*/\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
+               inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
 \r
-               // Taxa and synonyms\r
-               long numberTaxaResults = 0L;\r
-               \r
-               Class<? extends TaxonBase> clazz = null;\r
-               List<String> propertyPath = new ArrayList<String>();\r
-               propertyPath.addAll(configurator.getTaxonPropertyPath());\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
+               // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!\r
+               if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {\r
+                   logger.error("This specificEpithet is NULL" + taxon.getTitleCache());\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(), propertyPath);\r
-                       }\r
+               inferredSynName.setGenusOrUninomial(synGenusName);\r
+                     \r
+               if (parentName.isInfraGeneric()){\r
+                       inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);\r
                }\r
-\r
-               if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }\r
-               \r
-               if(taxa != null){\r
-                       results.addAll(taxa);\r
+               if (taxonName.isSpecies()){\r
+                       inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
+               }else if (taxonName.isInfraSpecific()){\r
+                       inferredSynName.setSpecificEpithet(synSpecificEpithet);\r
+                       inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);\r
                }\r
                \r
-               numberOfResults += numberTaxaResults;\r
+               inferredEpithet = Synonym.NewInstance(inferredSynName, null);\r
+               \r
+               // Set the sourceReference\r
+               inferredEpithet.setSec(sourceReference);\r
 \r
-               // Names without taxa \r
-               if (configurator.isDoNamesWithoutTaxa()) {\r
-            int numberNameResults = 0;\r
-           \r
-                       List<? extends TaxonNameBase<?,?>> names = \r
-                               nameDao.findByName(configurator.getSearchString(), 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
+               /* Add the original source\r
+               if (idInSource != null) {\r
+                   IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);\r
+\r
+                   // Add the citation\r
+                   Reference citation = getCitation(syn);\r
+                   if (citation != null) {\r
+                       originalSource.setCitation(citation);\r
+                       inferredEpithet.addSource(originalSource);\r
+                   }\r
+               }*/\r
+               String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
+                     \r
+               IdentifiableSource originalSource;\r
+               originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
                \r
-               // Taxa from common names\r
+               inferredEpithet.addSource(originalSource);\r
                \r
-               if (configurator.isDoTaxaByCommonNames()) {\r
-                       taxa = null;\r
-                       numberTaxaResults = 0;\r
-                       numberTaxaResults = dao.countTaxaByCommonName(configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(), configurator.getNamedAreas());\r
-                       if(numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){\r
-                               taxa = dao.getTaxaByCommonName(configurator.getSearchString(), configurator.getTaxonomicTree(), 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
+               originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\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
+               inferredSynName.addSource(originalSource);\r
+               \r
+               \r
+               \r
+               taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());\r
+               \r
+               inferredSynName.generateTitle();\r
+               return inferredEpithet;\r
        }\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, size, height, widthOrDuration, mimeTypes));\r
-                                       \r
-                               }\r
-                       }\r
-               }\r
-               return medRep;\r
-       }\r
+    /**\r
+     * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.\r
+     * Very likely only useful for createInferredSynonyms().\r
+     * @param uuid\r
+     * @param zooHashMap\r
+     * @return\r
+     */\r
+    private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {\r
+        ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);\r
+        if (taxonName == null) {\r
+            taxonName = zooHashMap.get(uuid);\r
+        }\r
+        return taxonName;\r
+    }\r
+    \r
+    /**\r
+     * Returns the idInSource for a given Synonym.\r
+     * @param syn\r
+     */\r
+    private String getIdInSource(TaxonBase taxonBase) {\r
+        String idInSource = null;\r
+        Set<IdentifiableSource> sources = taxonBase.getSources();\r
+        if (sources.size() == 1) {\r
+            IdentifiableSource source = sources.iterator().next();\r
+            if (source != null) {\r
+                idInSource  = source.getIdInSource();\r
+            }\r
+        } else if (sources.size() > 1) {\r
+            int count = 1;\r
+            idInSource = "";\r
+            for (IdentifiableSource source : sources) {\r
+                idInSource += source.getIdInSource();\r
+                if (count < sources.size()) {\r
+                    idInSource += "; ";\r
+                }\r
+                count++;\r
+            }\r
+        }\r
 \r
-       public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
-               return this.dao.findById(listOfIDs);\r
-       }\r
+        return idInSource;\r
+    }\r
+    \r
 \r
-       public int countAllRelationships() {\r
-               return this.dao.countAllRelationships();\r
-       }\r
+    /**\r
+     * Returns the citation for a given Synonym.\r
+     * @param syn\r
+     */\r
+    private Reference getCitation(Synonym syn) {\r
+        Reference citation = null;\r
+        Set<IdentifiableSource> sources = syn.getSources();\r
+        if (sources.size() == 1) {\r
+            IdentifiableSource source = sources.iterator().next();\r
+            if (source != null) {\r
+                citation = source.getCitation();\r
+            }\r
+        } else if (sources.size() > 1) {\r
+            logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");\r
+        }\r
 \r
-       public List<Synonym> createAllInferredSynonyms(TaxonomicTree tree,\r
-                       Taxon taxon) {\r
-               \r
-               return this.dao.createAllInferredSynonyms(taxon, tree);\r
-       }\r
+        return citation;\r
+    }\r
+    \r
+    public List<Synonym>  createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){\r
+        List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
 \r
-       public List<Synonym> createInferredSynonyms(TaxonomicTree tree, Taxon taxon, SynonymRelationshipType type) {\r
-               \r
-               return this.dao.createInferredSynonyms(taxon, tree, type);\r
-       }\r
+        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));\r
+        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));\r
+        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));\r
+\r
+        return inferredSynonyms;\r
+    }\r
 \r
-       public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {\r
-               \r
-               return this.dao.findIdenticalTaxonNames(propertyPath);\r
-       }\r
-       \r
-       public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {\r
-               \r
-               return this.dao.findIdenticalNamesNew(propertyPath);\r
-       }\r
-       \r
-       public String getPhylumName(TaxonNameBase name){\r
-               return this.dao.getPhylumName(name);\r
-       }\r
-       \r
-       private class TaxonAndNameComparator implements Comparator{\r
 \r
-               public int compare(Object arg0, Object arg1) {\r
-                       IdentifiableEntity castArg0 = (IdentifiableEntity) arg0;\r
-                       IdentifiableEntity castArg1 = (IdentifiableEntity) arg1;\r
-                       return castArg0.compareTo(castArg1);\r
-               }\r
-               \r
-       }\r
 \r
-       public long deleteSynonymRelationships(Synonym syn) {\r
-               \r
-               return dao.deleteSynonymRelationships(syn);\r
-       }\r
 \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