include trunk
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index fb6ba83c1c3c72340d8860c7fbba3e0b62ca0486..3c6cadf4554c2aa0a7c590af0f30e2ad6024be48 100644 (file)
@@ -1,3 +1,4 @@
+// $Id$\r
 /**\r
 * Copyright (C) 2007 EDIT\r
 * European Distributed Institute of Taxonomy \r
 \r
 package eu.etaxonomy.cdm.api.service;\r
 \r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.HashSet;\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.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.impl.TaxonServiceConfiguratorImpl;\r
+import eu.etaxonomy.cdm.api.service.pager.Pager;\r
+import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
+import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;\r
+import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
+import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
+import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.Feature;\r
+import eu.etaxonomy.cdm.model.description.FeatureNode;\r
+import eu.etaxonomy.cdm.model.description.FeatureTree;\r
+import eu.etaxonomy.cdm.model.description.Scope;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.media.Media;\r
+import eu.etaxonomy.cdm.model.media.MediaRepresentation;\r
+import eu.etaxonomy.cdm.model.media.MediaUtils;\r
+import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
+import eu.etaxonomy.cdm.model.name.NonViralName;\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.taxon.Synonym;\r
@@ -22,171 +52,607 @@ import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;\r
 import eu.etaxonomy.cdm.model.taxon.Taxon;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
+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.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
-\r
-import java.util.Collection;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.UUID;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
 \r
 \r
+/**\r
+ * @author a.kohlbecker\r
+ * @date 10.09.2010\r
+ *\r
+ */\r
 @Service\r
-@Transactional(readOnly = true)\r
-public class TaxonServiceImpl extends ServiceBase<TaxonBase> implements ITaxonService {\r
-       static Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
-       \r
-       private ITaxonDao taxonDao;\r
-       \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
-       protected void setDao(ITaxonDao dao) {\r
-               this.dao = dao;\r
-               this.taxonDao = dao;\r
-       }\r
+       private ITaxonNameDao nameDao;\r
        \r
+       @Autowired \r
+       private IDescriptionDao descriptionDao;\r
 \r
-       public TaxonBase getTaxonByUuid(UUID uuid) {\r
-               return super.getCdmObjectByUuid(uuid); \r
+       @Autowired\r
+       private IOrderedTermVocabularyDao orderedVocabularyDao;\r
+       \r
+       /**\r
+        * Constructor\r
+        */\r
+       public TaxonServiceImpl(){\r
+               if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }\r
        }\r
 \r
-       @Transactional(readOnly = false)\r
-       public UUID saveTaxon(TaxonBase taxon) {\r
-               return super.saveCdmObject(taxon);\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
-       @Transactional(readOnly = false)\r
-       public Map<UUID, TaxonBase> saveTaxonAll(Collection<TaxonBase> taxonCollection){\r
-               return saveCdmObjectAll(taxonCollection);\r
+       /**\r
+        * FIXME Candidate for harmonization\r
+        * list(Synonym.class, ...)\r
+        *  (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)\r
+        */\r
+       public List<Synonym> getAllSynonyms(int limit, int start) {\r
+               return dao.getAllSynonyms(limit, start);\r
        }\r
-\r
        \r
-\r
-       @Transactional(readOnly = false)\r
-       public UUID removeTaxon(TaxonBase taxon) {\r
-               return super.removeCdmObject(taxon);\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
-       public List<TaxonBase> searchTaxaByName(String name, ReferenceBase sec) {\r
-               return taxonDao.getTaxaByName(name, sec);\r
-       }\r
 \r
-       /* (non-Javadoc)\r
+       /**\r
+        * FIXME Candidate for harmonization\r
+        * merge with getRootTaxa(ReferenceBase sec, ..., ...)\r
+        *  (non-Javadoc)\r
         * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase)\r
         */\r
        public List<Taxon> getRootTaxa(ReferenceBase sec){\r
                return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true);\r
        }\r
 \r
-       \r
-       \r
-       /* (non-Javadoc)\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 taxonDao.getRootTaxa(sec, cdmFetch, onlyWithChildren);\r
+               return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);\r
+       }\r
+       \r
+       /**\r
+        * FIXME Candidate for harmonization\r
+        * merge with getRootTaxa(ReferenceBase sec, ..., ...)\r
+        *  (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)\r
+        */\r
+       public List<Taxon> getRootTaxa(ReferenceBase sec, boolean onlyWithChildren,\r
+                       boolean withMisapplications) {\r
+               return dao.getRootTaxa(sec, null, onlyWithChildren, withMisapplications);\r
+       }\r
+\r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)\r
+        */\r
+       public List<Taxon> getRootTaxa(Rank rank, ReferenceBase sec, boolean onlyWithChildren,\r
+                       boolean withMisapplications, List<String> propertyPaths) {\r
+               return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);\r
+       }\r
+\r
+       public List<RelationshipBase> getAllRelationships(int limit, int start){\r
+               return dao.getAllRelationships(limit, start);\r
+       }\r
+       \r
+       /**\r
+        * 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
        /* (non-Javadoc)\r
         * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)\r
         */\r
        @Transactional(readOnly = false)\r
-       public Synonym makeTaxonSynonym(Taxon oldTaxon, Taxon newAcceptedTaxon, SynonymRelationshipType synonymType, ReferenceBase citation, String citationMicroReference) {\r
-               if (oldTaxon == null || newAcceptedTaxon == null || oldTaxon.getName() == null){\r
-                       return null;\r
+       public Synonym changeAcceptedTaxonToSynonym(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, ReferenceBase citation, String citationMicroReference) {\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
+               if(oldTaxonNode.equals(newAcceptedTaxonNode)){\r
+                       throw new IllegalArgumentException("Taxon can not be made synonym of its own.");\r
+               }\r
+               \r
+               Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());\r
+               Taxon newAcceptedTaxon = (Taxon) HibernateProxyHelper.deproxy(newAcceptedTaxonNode.getTaxon());\r
+               \r
                // Move oldTaxon to newTaxon\r
-               TaxonNameBase synonymName = oldTaxon.getName();\r
-               if (synonymType == null){\r
+               TaxonNameBase<?,?> synonymName = oldTaxon.getName();\r
+               if (synonymRelationshipType == null){\r
                        if (synonymName.isHomotypic(newAcceptedTaxon.getName())){\r
-                               synonymType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+                               synonymRelationshipType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
                        }else{\r
-                               //TODO synonymType \r
-                               synonymType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+                               synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
                        }\r
                }\r
-               SynonymRelationship synRel = newAcceptedTaxon.addSynonymName(synonymName, synonymType, citation, citationMicroReference);\r
+               SynonymRelationship synonmyRelationship = newAcceptedTaxon.addSynonymName(synonymName, synonymRelationshipType, citation, citationMicroReference);\r
                \r
                //Move Synonym Relations to new Taxon\r
                for(SynonymRelationship synRelation : oldTaxon.getSynonymRelations()){\r
-                       //TODO citation and microcitation\r
-                       newAcceptedTaxon.addSynonym(synRelation.getSynonym(), synRelation.getType(), null, null);\r
+                       newAcceptedTaxon.addSynonym(synRelation.getSynonym(), synRelation.getType(), \r
+                                       synRelation.getCitation(), synRelation.getCitationMicroReference());\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
+               }\r
+               \r
                //Move Taxon RelationShips to new Taxon\r
-               for(TaxonRelationship taxonRelation : oldTaxon.getTaxonRelations()){\r
-                       //CHILDREN\r
-                       if (taxonRelation.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){\r
-                               if (taxonRelation.getFromTaxon() == oldTaxon){\r
-                                       oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addTaxonomicChild(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else{\r
-                                       logger.warn("Taxon is not part of its own Taxonrelationship");\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
+               }\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
+       /*\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
+       /*\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
+       }\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
+               return fromTaxon;\r
+       }\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
+\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
+       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
+       public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
+               Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
+               \r
+               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+               if(numberOfResults > 0) { // no point checking again\r
+                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo); \r
+               }\r
+               return results;\r
+       }\r
+       \r
+       public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);\r
+               \r
+               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+               if(numberOfResults > 0) { // no point checking again\r
+                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo); \r
+               }\r
+               return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+       }\r
+       \r
+       public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
+               Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
+               \r
+               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+               if(numberOfResults > 0) { // no point checking again\r
+                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom); \r
+               }\r
+               return results;\r
+       }\r
+       \r
+       public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);\r
+               \r
+               List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
+               if(numberOfResults > 0) { // no point checking again\r
+                       results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom); \r
+               }\r
+               return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
+       }\r
+\r
+       public Pager<SynonymRelationship> getSynonyms(Taxon taxon,      SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        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
+       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
+       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
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)\r
+        */\r
+       public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {\r
+               \r
+               List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();\r
+               int numberOfResults = 0; // overall number of results (as opposed to number of results per page)\r
+               List<TaxonBase> taxa = null; \r
+\r
+               // Taxa and synonyms\r
+               long numberTaxaResults = 0L;\r
+               \r
+               Class<? extends TaxonBase> clazz = null;\r
+               List<String> propertyPath = new ArrayList<String>();\r
+               if(configurator.getTaxonPropertyPath() != null){\r
+               propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+               }\r
+               if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {\r
+                       clazz = TaxonBase.class;\r
+                       //propertyPath.addAll(configurator.getTaxonPropertyPath());\r
+                       //propertyPath.addAll(configurator.getSynonymPropertyPath());\r
+               } else if(configurator.isDoTaxa()) {\r
+                       clazz = Taxon.class;\r
+                       //propertyPath = configurator.getTaxonPropertyPath();\r
+               } else if (configurator.isDoSynonyms()) {\r
+                       clazz = Synonym.class;\r
+                       //propertyPath = configurator.getSynonymPropertyPath();\r
+               }\r
+               \r
+               if(clazz != null){\r
+                       if(configurator.getPageSize() != null){ // no point counting if we need all anyway\r
+                       numberTaxaResults = \r
+                               dao.countTaxaByName(clazz, \r
+                                       configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(),\r
+                                       configurator.getNamedAreas());\r
+                       }\r
+                       if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results\r
+                               taxa = dao.getTaxaByName(clazz, \r
+                                       configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(),\r
+                                       configurator.getNamedAreas(), configurator.getPageSize(), \r
+                                       configurator.getPageNumber(), propertyPath);\r
+                       }\r
+               }\r
+\r
+               if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }\r
+               \r
+               if(taxa != null){\r
+                       results.addAll(taxa);\r
+               }\r
+               \r
+               numberOfResults += numberTaxaResults;\r
+\r
+               // Names without taxa \r
+               if (configurator.isDoNamesWithoutTaxa()) {\r
+            int numberNameResults = 0;\r
+           \r
+                       List<? extends TaxonNameBase<?,?>> names = \r
+                               nameDao.findByName(configurator.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
+               \r
+               // Taxa from common names\r
+               \r
+               if (configurator.isDoTaxaByCommonNames()) {\r
+                       taxa = null;\r
+                       numberTaxaResults = 0;\r
+                       if(configurator.getPageSize() != null){// no point counting if we need all anyway\r
+                       numberTaxaResults = dao.countTaxaByCommonName(configurator.getSearchString(), configurator.getTaxonomicTree(), configurator.getMatchMode(), configurator.getNamedAreas());\r
+                       }\r
+                       if(configurator.getPageSize() == null || 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
+               \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
+       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
-                       //MISAPPLIED NAMES\r
-                       if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIEDNAMEFOR())){\r
-                               if (taxonRelation.getFromTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-                                       newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-                                       oldTaxon.removeTaxonRelation(taxonRelation);\r
-                               }else{\r
-                                       logger.warn("Taxon is not part of its own Taxonrelationship");\r
+               }\r
+               return medRep;\r
+       }\r
+\r
+       public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
+               return this.dao.findById(listOfIDs);\r
+       }\r
+\r
+       public int countAllRelationships() {\r
+               return this.dao.countAllRelationships();\r
+       }\r
+\r
+       public List<Synonym> createAllInferredSynonyms(TaxonomicTree tree,\r
+                       Taxon taxon) {\r
+               \r
+               return this.dao.createAllInferredSynonyms(taxon, tree);\r
+       }\r
+\r
+       public List<Synonym> createInferredSynonyms(TaxonomicTree tree, Taxon taxon, SynonymRelationshipType type) {\r
+               \r
+               return this.dao.createInferredSynonyms(taxon, tree, type);\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
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.api.service.ITaxonService#matchToTaxon(eu.etaxonomy.cdm.model.name.NonViralName)\r
+        */\r
+       @Override\r
+       public Taxon findBestMatchingTaxon(String taxonName) {\r
+               \r
+               Taxon matchedTaxon = null;\r
+               try{\r
+                       // 1. search for acceptet taxa\r
+                       List<TaxonBase> taxonList = dao.findByNameTitleCache(Taxon.class, taxonName, null, MatchMode.EXACT, null, 0, null, null);\r
+                       for(IdentifiableEntity taxonBaseCandidate : taxonList){\r
+                               if(taxonBaseCandidate instanceof Taxon){\r
+                                       matchedTaxon = (Taxon)taxonBaseCandidate;\r
+                                       if(taxonList.size() > 1){\r
+                                               logger.info(taxonList.size() + " TaxonBases found, using first accepted Taxon: " + matchedTaxon.getTitleCache());\r
+                                               return matchedTaxon;\r
+                                       } else {\r
+                                               logger.info("using accepted Taxon: " + matchedTaxon.getTitleCache());\r
+                                               return matchedTaxon;\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
-                       //Concept Relationships\r
-                       //FIXME implement\r
-//                     if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIEDNAMEFOR())){\r
-//                             if (taxonRelation.getFromTaxon() == oldTaxon){\r
-//                                     newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-//                             }else if(taxonRelation.getToTaxon() == oldTaxon){\r
-//                                     newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());\r
-//                                     oldTaxon.removeTaxonRelation(taxonRelation);\r
-//                             }else{\r
-//                                     logger.warn("Taxon is not part of its own Taxonrelationship");\r
-//                             }\r
-//                     }\r
-               }\r
-               \r
-               //Move Descriptions to new Taxon\r
-               for(TaxonDescription taxDescription : oldTaxon.getDescriptions()){\r
-                       newAcceptedTaxon.addDescription(taxDescription);\r
-               }\r
-               //delete old Taxon\r
-               this.dao.saveOrUpdate(newAcceptedTaxon);\r
-//             FIXME implement\r
-               this.dao.delete(oldTaxon);\r
-               \r
-               //return\r
-               this.dao.flush();\r
-               return synRel.getSynonym();\r
-       }\r
-\r
-\r
-       public void generateTitleCache() {\r
-               generateTitleCache(true);\r
-       }\r
-       //TODO\r
-       public void generateTitleCache(boolean forceProtected) {\r
-               logger.warn("generateTitleCache not yet fully implemented!");\r
-//             for (TaxonBase tb : taxonDao.getAllTaxa(null,null)){\r
-//                     logger.warn("Old taxon title: " + tb.getTitleCache());\r
-//                     if (forceProtected || !tb.isProtectedTitleCache() ){\r
-//                             tb.setTitleCache(tb.generateTitle(), false);\r
-//                             taxonDao.update(tb);\r
-//                             logger.warn("New title: " + tb.getTitleCache());\r
-//                     }\r
-//             }\r
+                       \r
+                       // 2. search for synonyms\r
+                       List<TaxonBase> synonymList = dao.findByNameTitleCache(Synonym.class, taxonName, null, MatchMode.EXACT, null, 0, null, null);\r
+                       for(IdentifiableEntity taxonBase : synonymList){\r
+                               if(taxonBase instanceof Synonym){\r
+                                       Set<Taxon> acceptetdCandidates = ((Synonym)taxonBase).getAcceptedTaxa();\r
+                                       if(!acceptetdCandidates.isEmpty()){\r
+                                               matchedTaxon = acceptetdCandidates.iterator().next();\r
+                                               if(acceptetdCandidates.size() == 1){\r
+                                                       logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + matchedTaxon.getTitleCache());\r
+                                                       return matchedTaxon;\r
+                                               } else {\r
+                                                       logger.info("using accepted Taxon " +  matchedTaxon.getTitleCache() + "for synonym " + taxonBase.getTitleCache());\r
+                                                       return matchedTaxon;\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
+               } catch (Exception e){\r
+                       logger.error(e);\r
+               }\r
                \r
+               return matchedTaxon;\r
        }\r
 }\r