merge-update from trunk
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index 26f25646656337f3e2aef4fb0e0388597ec66623..dcb0b8f27907c7f3b87a3338218bef84210690a9 100644 (file)
@@ -12,46 +12,58 @@ package eu.etaxonomy.cdm.api.service;
 \r
 import java.io.IOException;\r
 import java.util.ArrayList;\r
+import java.util.EnumSet;\r
 import java.util.HashMap;\r
 import java.util.HashSet;\r
+import java.util.Iterator;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
+import javax.persistence.EntityNotFoundException;\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.BooleanClause.Occur;\r
+import org.apache.lucene.search.BooleanFilter;\r
 import org.apache.lucene.search.BooleanQuery;\r
+import org.apache.lucene.search.DocIdSet;\r
 import org.apache.lucene.search.Query;\r
+import org.apache.lucene.search.QueryWrapperFilter;\r
 import org.apache.lucene.search.SortField;\r
-import org.apache.lucene.search.TopDocs;\r
-import org.apache.lucene.search.grouping.TopGroups;\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.DeleteConfiguratorBase;\r
 import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.IncludedTaxonConfiguration;\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.SynonymDeletionConfigurator;\r
 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;\r
+import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator.ChildHandling;\r
+import eu.etaxonomy.cdm.api.service.dto.IncludedTaxaDTO;\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.ILuceneIndexToolProvider;\r
 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;\r
 import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;\r
+import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;\r
 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;\r
 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;\r
 import eu.etaxonomy.cdm.api.service.search.QueryFactory;\r
 import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;\r
+import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;\r
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;\r
+import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;\r
 import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;\r
 import eu.etaxonomy.cdm.model.CdmBaseType;\r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
@@ -59,25 +71,41 @@ import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
 import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;\r
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;\r
+import eu.etaxonomy.cdm.model.description.CommonTaxonName;\r
+import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.Distribution;\r
 import eu.etaxonomy.cdm.model.description.Feature;\r
 import eu.etaxonomy.cdm.model.description.IIdentificationKey;\r
 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
 import eu.etaxonomy.cdm.model.description.TaxonInteraction;\r
+import eu.etaxonomy.cdm.model.description.TaxonNameDescription;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\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.molecular.Amplification;\r
+import eu.etaxonomy.cdm.model.molecular.DnaSample;\r
+import eu.etaxonomy.cdm.model.molecular.Sequence;\r
+import eu.etaxonomy.cdm.model.molecular.SingleRead;\r
 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
+import eu.etaxonomy.cdm.model.name.NameRelationship;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
 import eu.etaxonomy.cdm.model.name.ZoologicalName;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;\r
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
+import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;\r
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
 import eu.etaxonomy.cdm.model.reference.Reference;\r
 import eu.etaxonomy.cdm.model.taxon.Classification;\r
+import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;\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
@@ -88,7 +116,10 @@ import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\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.initializer.AbstractBeanInitializer;\r
 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;\r
+import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;\r
+import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;\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
@@ -103,7 +134,7 @@ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
  *\r
  */\r
 @Service\r
-@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\r
+@Transactional(readOnly = true)\r
 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{\r
     private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
 \r
@@ -120,6 +151,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     @Autowired\r
     private INameService nameService;\r
 \r
+    @Autowired\r
+    private ITaxonNodeService nodeService;\r
+\r
     @Autowired\r
     private ICdmGenericDao genericDao;\r
 \r
@@ -129,6 +163,18 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     @Autowired\r
     private IOrderedTermVocabularyDao orderedVocabularyDao;\r
 \r
+    @Autowired\r
+    private IOccurrenceDao occurrenceDao;\r
+\r
+    @Autowired\r
+    private IClassificationDao classificationDao;\r
+\r
+    @Autowired\r
+    private AbstractBeanInitializer beanInitializer;\r
+\r
+    @Autowired\r
+    private ILuceneIndexToolProvider luceneIndexToolProvider;\r
+\r
     /**\r
      * Constructor\r
      */\r
@@ -140,36 +186,18 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * FIXME Candidate for harmonization\r
      * rename searchByName ?\r
      */\r
+    @Override\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
+    @Override\r
     public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {\r
         if (cdmFetch == null){\r
             cdmFetch = CdmFetch.NO_FETCH();\r
@@ -177,17 +205,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         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
+    @Override\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
+    @Override\r
     public List<RelationshipBase> getAllRelationships(int limit, int start){\r
         return dao.getAllRelationships(limit, start);\r
     }\r
@@ -196,6 +219,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * FIXME Candidate for harmonization\r
      * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?\r
      */\r
+    @Override\r
     @Deprecated\r
     public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {\r
 \r
@@ -212,6 +236,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)\r
      */\r
+    @Override\r
     @Transactional(readOnly = false)\r
     public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
 \r
@@ -232,7 +257,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+\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
@@ -251,10 +276,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
         SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
         List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);\r
+        Set<NameRelationship> basionymsAndReplacedSynonyms = synonymHomotypicGroup.getBasionymAndReplacedSynonymRelations();\r
 \r
         for (Synonym heteroSynonym : heteroSynonyms){\r
             if (synonym.equals(heteroSynonym)){\r
                 acceptedTaxon.removeSynonym(heteroSynonym, false);\r
+\r
             }else{\r
                 //move synonyms in same homotypic group to new accepted taxon\r
                 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);\r
@@ -262,12 +289,14 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         }\r
 \r
         //synonym.getName().removeTaxonBase(synonym);\r
-        //TODO correct delete handling still needs to be implemented / checked\r
+\r
         if (deleteSynonym){\r
 //                     deleteSynonym(synonym, taxon, false);\r
             try {\r
                 this.dao.flush();\r
-                this.delete(synonym);\r
+                SynonymDeletionConfigurator config = new SynonymDeletionConfigurator();\r
+                config.setDeleteNameIfPossible(false);\r
+                this.deleteSynonym(synonym, acceptedTaxon, config);\r
 \r
             } catch (Exception e) {\r
                 logger.info("Can't delete old synonym from database");\r
@@ -278,14 +307,15 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     }\r
 \r
 \r
+    @Override\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
+      /*  // remove synonym from taxon\r
         toTaxon.removeSynonym(synonym);\r
-\r
+*/\r
         // Create a taxon with synonym name\r
         Taxon fromTaxon = Taxon.NewInstance(synonymName, null);\r
 \r
@@ -294,7 +324,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \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
+       // synonym.getName().removeTaxonBase(synonym);\r
+        this.deleteSynonym(synonym, null);\r
 \r
         return fromTaxon;\r
     }\r
@@ -376,6 +407,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);\r
     }\r
 \r
+    @Override\r
     @Autowired\r
     protected void setDao(ITaxonDao dao) {\r
         this.dao = dao;\r
@@ -384,6 +416,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -398,6 +431,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -412,6 +446,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -425,6 +460,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -438,6 +474,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -451,6 +488,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -461,9 +499,134 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
     }\r
 \r
+    @Override\r
+    public List<Taxon> listAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,\r
+            List<OrderHint> orderHints, List<String> propertyPaths){\r
+        return pageAcceptedTaxaFor(synonymUuid, classificationUuid, pageSize, pageNumber, orderHints, propertyPaths).getRecords();\r
+    }\r
+\r
+    @Override\r
+    public Pager<Taxon> pageAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,\r
+            List<OrderHint> orderHints, List<String> propertyPaths){\r
+\r
+        List<Taxon> list = new ArrayList<Taxon>();\r
+        Long count = 0l;\r
+\r
+        Synonym synonym = null;\r
+\r
+        try {\r
+            synonym = (Synonym) dao.load(synonymUuid);\r
+        } catch (ClassCastException e){\r
+            throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid + " is not a Synonmy");\r
+        } catch (NullPointerException e){\r
+            throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid);\r
+        }\r
+\r
+        Classification classificationFilter = null;\r
+        if(classificationUuid != null){\r
+            try {\r
+            classificationFilter = classificationDao.load(classificationUuid);\r
+            } catch (NullPointerException e){\r
+                throw new EntityNotFoundException("No Classification entity found for " + classificationUuid);\r
+            }\r
+            if(classificationFilter == null){\r
+\r
+            }\r
+        }\r
+\r
+        count = dao.countAcceptedTaxaFor(synonym, classificationFilter) ;\r
+        if(count > (pageSize * pageNumber)){\r
+            list = dao.listAcceptedTaxaFor(synonym, classificationFilter, pageSize, pageNumber, orderHints, propertyPaths);\r
+        }\r
+\r
+        return new DefaultPagerImpl<Taxon>(pageNumber, count.intValue(), pageSize, list);\r
+    }\r
+\r
+\r
+    /**\r
+     * @param taxon\r
+     * @param includeRelationships\r
+     * @param maxDepth\r
+     * @param limit\r
+     * @param starts\r
+     * @param propertyPaths\r
+     * @return an List which is not specifically ordered\r
+     */\r
+    @Override\r
+    public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,\r
+            Integer limit, Integer start, List<String> propertyPaths) {\r
+\r
+        Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<Taxon>(), maxDepth);\r
+        relatedTaxa.remove(taxon);\r
+        beanInitializer.initializeAll(relatedTaxa, propertyPaths);\r
+        return relatedTaxa;\r
+    }\r
+\r
+\r
+    /**\r
+     * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the\r
+     *  <code>taxon</code> supplied as parameter.\r
+     *\r
+     * @param taxon\r
+     * @param includeRelationships\r
+     * @param taxa\r
+     * @param maxDepth can be <code>null</code> for infinite depth\r
+     * @return\r
+     */\r
+    private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa, Integer maxDepth) {\r
+\r
+        if(taxa.isEmpty()) {\r
+            taxa.add(taxon);\r
+        }\r
+\r
+        if(includeRelationships.isEmpty()){\r
+            return taxa;\r
+        }\r
+\r
+        if(maxDepth != null) {\r
+            maxDepth--;\r
+        }\r
+        if(logger.isDebugEnabled()){\r
+            logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);\r
+        }\r
+        List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null);\r
+        for (TaxonRelationship taxRel : taxonRelationships) {\r
+\r
+            // skip invalid data\r
+            if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {\r
+                continue;\r
+            }\r
+            // filter by includeRelationships\r
+            for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {\r
+                if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) {\r
+                    if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {\r
+                        if(logger.isDebugEnabled()){\r
+                            logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());\r
+                        }\r
+                        taxa.add(taxRel.getToTaxon());\r
+                        if(maxDepth == null || maxDepth > 0) {\r
+                            taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth));\r
+                        }\r
+                    }\r
+                    if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {\r
+                        taxa.add(taxRel.getFromTaxon());\r
+                        if(logger.isDebugEnabled()){\r
+                            logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );\r
+                        }\r
+                        if(maxDepth == null || maxDepth > 0) {\r
+                            taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth));\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        return taxa;\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
+    @Override\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
@@ -478,6 +641,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -492,6 +656,28 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
      */\r
+    @Override\r
+    public List<List<Synonym>> getSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){\r
+         List<List<Synonym>> result = new ArrayList<List<Synonym>>();\r
+        Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
+\r
+        //homotypic\r
+        result.add(t.getHomotypicSynonymsByHomotypicGroup());\r
+\r
+        //heterotypic\r
+        List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();\r
+        for(HomotypicalGroup homotypicalGroup : homotypicalGroups){\r
+            result.add(t.getSynonymsInGroup(homotypicalGroup));\r
+        }\r
+\r
+        return result;\r
+\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
+    @Override\r
     public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){\r
         Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);\r
         return t.getHomotypicSynonymsByHomotypicGroup();\r
@@ -500,6 +686,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)\r
      */\r
+    @Override\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
@@ -510,6 +697,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return heterotypicSynonymyGroups;\r
     }\r
 \r
+    @Override\r
     public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){\r
 \r
         List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();\r
@@ -534,6 +722,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)\r
      */\r
+    @Override\r
     public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {\r
 \r
         List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();\r
@@ -603,10 +792,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
                 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
+                List<Taxon> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());\r
+                taxa.addAll(commonNameResults);\r
             }\r
             if(taxa != null){\r
                 results.addAll(taxa);\r
@@ -626,6 +813,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\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
@@ -647,49 +835,158 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)\r
      */\r
-    public List<Media> listTaxonDescriptionMedia(Taxon taxon, boolean limitToGalleries, List<String> propertyPath){\r
+    @Override\r
+    public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){\r
+        return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);\r
+    }\r
 \r
-        Pager<TaxonDescription> p =\r
-                    descriptionService.getTaxonDescriptions(taxon, null, null, null, null, propertyPath);\r
 \r
-        // pars the media and quality parameters\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)\r
+     */\r
+    @Override\r
+    public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,\r
+            Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,\r
+            Boolean includeTaxonNameDescriptions, List<String> propertyPath) {\r
 \r
+        logger.trace("listMedia() - START");\r
 \r
-        // collect all media of the given taxon\r
+        Set<Taxon> taxa = new HashSet<Taxon>();\r
         List<Media> taxonMedia = new ArrayList<Media>();\r
-        List<Media> taxonGalleryMedia = new ArrayList<Media>();\r
-        for(TaxonDescription desc : p.getRecords()){\r
+        List<Media> nonImageGalleryImages = new ArrayList<Media>();\r
+\r
+        if (limitToGalleries == null) {\r
+            limitToGalleries = false;\r
+        }\r
 \r
-            if(desc.isImageGallery()){\r
-                for(DescriptionElementBase element : desc.getElements()){\r
-                    for(Media media : element.getMedia()){\r
-                        taxonGalleryMedia.add(media);\r
+        // --- resolve related taxa\r
+        if (includeRelationships != null && ! includeRelationships.isEmpty()) {\r
+            logger.trace("listMedia() - resolve related taxa");\r
+            taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null);\r
+        }\r
+\r
+        taxa.add((Taxon) dao.load(taxon.getUuid()));\r
+\r
+        if(includeTaxonDescriptions != null && includeTaxonDescriptions){\r
+            logger.trace("listMedia() - includeTaxonDescriptions");\r
+            List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();\r
+            // --- TaxonDescriptions\r
+            for (Taxon t : taxa) {\r
+                taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));\r
+            }\r
+            for (TaxonDescription taxonDescription : taxonDescriptions) {\r
+                if (!limitToGalleries || taxonDescription.isImageGallery()) {\r
+                    for (DescriptionElementBase element : taxonDescription.getElements()) {\r
+                        for (Media media : element.getMedia()) {\r
+                            if(taxonDescription.isImageGallery()){\r
+                                taxonMedia.add(media);\r
+                            }\r
+                            else{\r
+                                nonImageGalleryImages.add(media);\r
+                            }\r
+                        }\r
                     }\r
                 }\r
-            } else if(!limitToGalleries){\r
-                for(DescriptionElementBase element : desc.getElements()){\r
-                    for(Media media : element.getMedia()){\r
-                        taxonMedia.add(media);\r
+            }\r
+            //put images from image gallery first (#3242)\r
+            taxonMedia.addAll(nonImageGalleryImages);\r
+        }\r
+\r
+\r
+        if(includeOccurrences != null && includeOccurrences) {\r
+            logger.trace("listMedia() - includeOccurrences");\r
+            Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<SpecimenOrObservationBase>();\r
+            // --- Specimens\r
+            for (Taxon t : taxa) {\r
+                specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));\r
+            }\r
+            for (SpecimenOrObservationBase occurrence : specimensOrObservations) {\r
+\r
+//             direct media removed from specimen #3597\r
+//              taxonMedia.addAll(occurrence.getMedia());\r
+\r
+                // SpecimenDescriptions\r
+                Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();\r
+                for (DescriptionBase specimenDescription : specimenDescriptions) {\r
+                    if (!limitToGalleries || specimenDescription.isImageGallery()) {\r
+                        Set<DescriptionElementBase> elements = specimenDescription.getElements();\r
+                        for (DescriptionElementBase element : elements) {\r
+                            for (Media media : element.getMedia()) {\r
+                                taxonMedia.add(media);\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+\r
+                // Collection\r
+                //TODO why may collections have media attached? #\r
+                if (occurrence.isInstanceOf(DerivedUnit.class)) {\r
+                    DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);\r
+                    if (derivedUnit.getCollection() != null){\r
+                        taxonMedia.addAll(derivedUnit.getCollection().getMedia());\r
+                    }\r
+                }\r
+\r
+                // pherograms & gelPhotos\r
+                if (occurrence.isInstanceOf(DnaSample.class)) {\r
+                    DnaSample dnaSample = CdmBase.deproxy(occurrence, DnaSample.class);\r
+                    Set<Sequence> sequences = dnaSample.getSequences();\r
+                    //we do show only those gelPhotos which lead to a consensus sequence\r
+                    for (Sequence sequence : sequences) {\r
+                        Set<Media> dnaRelatedMedia = new HashSet<Media>();\r
+                        for (SingleRead singleRead : sequence.getSingleReads()){\r
+                            Amplification amplification = singleRead.getAmplification();\r
+                            dnaRelatedMedia.add(amplification.getGelPhoto());\r
+                            dnaRelatedMedia.add(singleRead.getPherogram());\r
+                            dnaRelatedMedia.remove(null);\r
+                        }\r
+                        taxonMedia.addAll(dnaRelatedMedia);\r
                     }\r
                 }\r
+\r
             }\r
+        }\r
 \r
+        if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {\r
+            logger.trace("listMedia() - includeTaxonNameDescriptions");\r
+            // --- TaxonNameDescription\r
+            Set<TaxonNameDescription> nameDescriptions = new HashSet<TaxonNameDescription>();\r
+            for (Taxon t : taxa) {\r
+                nameDescriptions .addAll(t.getName().getDescriptions());\r
+            }\r
+            for(TaxonNameDescription nameDescription: nameDescriptions){\r
+                if (!limitToGalleries || nameDescription.isImageGallery()) {\r
+                    Set<DescriptionElementBase> elements = nameDescription.getElements();\r
+                    for (DescriptionElementBase element : elements) {\r
+                        for (Media media : element.getMedia()) {\r
+                            taxonMedia.add(media);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
         }\r
 \r
-        taxonGalleryMedia.addAll(taxonMedia);\r
-        return taxonGalleryMedia;\r
+\r
+        logger.trace("listMedia() - initialize");\r
+        beanInitializer.initializeAll(taxonMedia, propertyPath);\r
+\r
+        logger.trace("listMedia() - END");\r
+\r
+        return taxonMedia;\r
     }\r
 \r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)\r
      */\r
+    @Override\r
     public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
-        return this.dao.findById(listOfIDs);\r
+        return this.dao.listByIds(listOfIDs, null, null, null, null);\r
     }\r
 \r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)\r
      */\r
+    @Override\r
     public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){\r
         return this.dao.findByUuid(uuid, null ,propertyPaths);\r
     }\r
@@ -697,6 +994,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()\r
      */\r
+    @Override\r
     public int countAllRelationships() {\r
         return this.dao.countAllRelationships();\r
     }\r
@@ -707,6 +1005,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)\r
      */\r
+    @Override\r
     public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {\r
         return this.dao.findIdenticalTaxonNames(propertyPath);\r
     }\r
@@ -716,147 +1015,399 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * @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
+    public DeleteResult deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config, Classification classification)  {\r
+       DeleteResult result = new DeleteResult();\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
+        List<String> referencedObjects = isDeletable(taxon, config);\r
 \r
-            //         SynonymRelationShip\r
+        if (referencedObjects.isEmpty()){\r
+            // --- DeleteSynonymRelations\r
             if (config.isDeleteSynonymRelations()){\r
                 boolean removeSynonymNameFromHomotypicalGroup = false;\r
-                for (SynonymRelationship synRel : taxon.getSynonymRelations()){\r
+                // use tmp Set to avoid concurrent modification\r
+                Set<SynonymRelationship> synRelsToDelete = new HashSet<SynonymRelationship>();\r
+                synRelsToDelete.addAll(taxon.getSynonymRelations());\r
+                for (SynonymRelationship synRel : synRelsToDelete){\r
                     Synonym synonym = synRel.getSynonym();\r
+                    // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL\r
+                    // this will cause hibernate to delete the relationship since\r
+                    // the SynonymRelationship field on both is annotated with removeOrphan\r
+                    // so no further explicit deleting of the relationship should be done here\r
                     taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);\r
+\r
+                    // --- DeleteSynonymsIfPossible\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
+                        SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();\r
+                        deleteSynonym(synonym, taxon, synConfig);\r
                     }\r
+                    // relationship will be deleted by hibernate automatically,\r
+                    // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797\r
+                    // else{\r
+                    //     deleteSynonymRelationships(synonym, taxon);\r
+                    // }\r
                 }\r
             }\r
 \r
-            //         TaxonRelationship\r
+            // --- DeleteTaxonRelationships\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
+                    String message = "Taxon can't be deleted as it is related to another taxon. " +\r
+                            "Remove taxon from all relations to other taxa prior to deletion.";\r
+                   // throw new ReferencedObjectUndeletableException(message);\r
                 }\r
-            }\r
+            } else{\r
+                for (TaxonRelationship taxRel: taxon.getTaxonRelations()){\r
+                    if (config.isDeleteMisappliedNamesAndInvalidDesignations()){\r
+                        if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){\r
+                            if (taxon.equals(taxRel.getToTaxon())){\r
+                                this.deleteTaxon(taxRel.getFromTaxon(), config, classification);\r
+                            }\r
+                        }\r
+                    }\r
+                    taxon.removeTaxonRelation(taxRel);\r
+                    /*if (taxFrom.equals(taxon)){\r
+                        try{\r
+                            this.deleteTaxon(taxTo, taxConf, classification);\r
+                        } catch(DataChangeNoRollbackException e){\r
+                            logger.debug("A related taxon will not be deleted." + e.getMessage());\r
+                        }\r
+                    } else {\r
+                        try{\r
+                            this.deleteTaxon(taxFrom, taxConf, classification);\r
+                        } catch(DataChangeNoRollbackException e){\r
+                            logger.debug("A related taxon will not be deleted." + e.getMessage());\r
+                        }\r
 \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
+            if (config.isDeleteDescriptions()){\r
+                Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
+                List<TaxonDescription> removeDescriptions = new ArrayList<TaxonDescription>();\r
+                for (TaxonDescription desc: descriptions){\r
+                    //TODO use description delete configurator ?\r
+                    //FIXME check if description is ALWAYS deletable\r
+                    if (desc.getDescribedSpecimenOrObservation() != null){\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
+                    removeDescriptions.add(desc);\r
+                    descriptionService.delete(desc);\r
+\r
+                }\r
+                for (TaxonDescription desc: removeDescriptions){\r
+                    taxon.removeDescription(desc);\r
+                }\r
+            }\r
+\r
+\r
+         /*   //check references with only reverse mapping\r
+        String message = checkForReferences(taxon);\r
+        if (message != null){\r
+            //throw new ReferencedObjectUndeletableException(message.toString());\r
+        }*/\r
+\r
+         if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null )){\r
+                //if (taxon.getTaxonNodes().size() > 0){\r
+                   // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";\r
+                   // throw new ReferencedObjectUndeletableException(message);\r
+                //}\r
+            }else{\r
+                if (taxon.getTaxonNodes().size() != 0){\r
+                    Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
+                    Iterator<TaxonNode> iterator = nodes.iterator();\r
+                    TaxonNode node = null;\r
+                    boolean deleteChildren;\r
+                    if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){\r
+                        deleteChildren = true;\r
+                    }else {\r
+                        deleteChildren = false;\r
+                    }\r
+                    boolean success = true;\r
+                    if (!config.isDeleteInAllClassifications() && !(classification == null)){\r
+                        while (iterator.hasNext()){\r
+                            node = iterator.next();\r
+                            if (node.getClassification().equals(classification)){\r
+                                break;\r
                             }\r
+                            node = null;\r
                         }\r
+                        if (node != null){\r
+                            success =taxon.removeTaxonNode(node, deleteChildren);\r
+                            nodeService.delete(node);\r
+                        } else {\r
+                               result.setError();\r
+                               result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));\r
+                        }\r
+                    } else if (config.isDeleteInAllClassifications()){\r
+                        Set<ITaxonTreeNode> nodesList = new HashSet<ITaxonTreeNode>();\r
+                        nodesList.addAll(taxon.getTaxonNodes());\r
+\r
+                            for (ITaxonTreeNode treeNode: nodesList){\r
+                                TaxonNode taxonNode = (TaxonNode) treeNode;\r
+                                if(!deleteChildren){\r
+                                   /* Object[] childNodes = taxonNode.getChildNodes().toArray();\r
+                                    //nodesList.addAll(taxonNode.getChildNodes());\r
+                                    for (Object childNode: childNodes){\r
+                                        TaxonNode childNodeCast = (TaxonNode) childNode;\r
+                                        deleteTaxon(childNodeCast.getTaxon(), config, classification);\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
+                                    /*for (TaxonNode childNode: taxonNode.getChildNodes()){\r
+                                        deleteTaxon(childNode.getTaxon(), config, classification);\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
+                                   // taxon.removeTaxonNode(taxonNode);\r
+                                    //nodeService.delete(taxonNode);\r
+                                } else{\r
+                                    */\r
+                                    Object[] childNodes = taxonNode.getChildNodes().toArray();\r
+                                    for (Object childNode: childNodes){\r
+                                        TaxonNode childNodeCast = (TaxonNode) childNode;\r
+                                        taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());\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
+                                    //taxon.removeTaxonNode(taxonNode);\r
+                                }\r
+                            }\r
+                        config.getTaxonNodeConfig().setDeleteTaxon(false);\r
+                        DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);\r
+                        if (!resultNodes.isOk()){\r
+                               result.addExceptions(resultNodes.getExceptions());\r
+                               result.setStatus(resultNodes.getStatus());\r
+                        }\r
+                    }\r
+                    if (!success){\r
+                        result.setError();\r
+                        result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));\r
+                    }\r
                 }\r
             }\r
 \r
 \r
+             //PolytomousKey TODO\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
+                    //TaxonNameBase name = nameService.find(taxon.getName().getUuid());\r
+                    TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName());\r
+                    //check whether taxon will be deleted or not\r
+                    if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && name != null ){\r
+                        taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);\r
+                        name.removeTaxonBase(taxon);\r
+                        nameService.saveOrUpdate(name);\r
+                        DeleteResult nameResult = new DeleteResult();\r
+\r
+                        nameResult = nameService.delete(name, config.getNameDeletionConfig());\r
+\r
+                        if (nameResult.isError()){\r
+                               //result.setError();\r
+                               result.addRelatedObject(name);\r
+                               result.addExceptions(nameResult.getExceptions());\r
+                        }\r
+\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
+                    taxon.removeDescription(desc);\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 observations";\r
+                            throw new ReferencedObjectUndeletableException(message);\r
+    }\r
+                    }\r
+                }*/\r
+\r
+            if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0)  ){\r
+               try{\r
+                       UUID uuid = dao.delete(taxon);\r
+\r
+               }catch(Exception e){\r
+                       result.addException(e);\r
+                       result.setError();\r
+\r
+               }\r
+            } else {\r
+               result.setError();\r
+               result.addException(new Exception("The Taxon can't be deleted."));\r
+\r
             }\r
+        }else {\r
+               List<Exception> exceptions = new ArrayList<Exception>();\r
+               for (String message: referencedObjects){\r
+                       ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message);\r
+                       exceptions.add(exception);\r
+               }\r
+               result.addExceptions(exceptions);\r
+               result.setError();\r
+\r
+        }\r
+        return result;\r
 \r
     }\r
 \r
+    private String checkForReferences(Taxon taxon){\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" + taxon.getTitleCache() + "can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+\r
+                return message;\r
+            }\r
+\r
+\r
+           /* //PolytomousKeyNode\r
+            if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+                String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";\r
+                return 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
+                return message;\r
+            }\r
+\r
+          //TaxonInteraction\r
+            if (referencingObject.isInstanceOf(DeterminationEvent.class)){\r
+                String message = "Taxon can't be deleted as it is used in a determination event";\r
+                return message;\r
+            }\r
+\r
+        }\r
+\r
+        referencingObjects = null;\r
+        return null;\r
+    }\r
+\r
+    private boolean checkForPolytomousKeys(Taxon taxon){\r
+        boolean result = false;\r
+        List<CdmBase> list = genericDao.getCdmBasesByFieldAndClass(PolytomousKeyNode.class, "taxon", taxon);\r
+        if (!list.isEmpty()) {\r
+            result = true;\r
+        }\r
+        return result;\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public UUID delete(Synonym syn){\r
+        UUID result = syn.getUuid();\r
+        this.deleteSynonym(syn, null);\r
+        return result;\r
+    }\r
+\r
+\r
+\r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)\r
      */\r
     @Transactional(readOnly = false)\r
     @Override\r
-    public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {\r
-        if (synonym == null){\r
-            return;\r
-        }\r
-        synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\r
+    public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {\r
+        return deleteSynonym(synonym, null, config);\r
 \r
-        //remove synonymRelationship\r
-        Set<Taxon> taxonSet = new HashSet<Taxon>();\r
-        if (taxon != null){\r
-            taxonSet.add(taxon);\r
-        }else{\r
-            taxonSet.addAll(synonym.getAcceptedTaxa());\r
+    }\r
+\r
+\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 DeleteResult deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) {\r
+        DeleteResult result = new DeleteResult();\r
+       if (synonym == null){\r
+               result.setAbort();\r
+               return result;\r
         }\r
-        for (Taxon relatedTaxon : taxonSet){\r
-//                     dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
-            relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);\r
+\r
+        if (config == null){\r
+            config = new SynonymDeletionConfigurator();\r
         }\r
-        this.saveOrUpdate(synonym);\r
+        List<String> messages = isDeletable(synonym, config);\r
+\r
+\r
+        if (messages.isEmpty()){\r
+            synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\r
+\r
+            //remove synonymRelationship\r
+            Set<Taxon> taxonSet = new HashSet<Taxon>();\r
+            if (taxon != null){\r
+                taxonSet.add(taxon);\r
+            }else{\r
+                taxonSet.addAll(synonym.getAcceptedTaxa());\r
+            }\r
+            for (Taxon relatedTaxon : taxonSet){\r
+    //                 dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
+                relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded());\r
+            }\r
+            this.saveOrUpdate(synonym);\r
+\r
+            //TODO remove name from homotypical group?\r
 \r
-        //TODO remove name from homotypical group?\r
+            //remove synonym (if necessary)\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
+            UUID uuid = null;\r
+            if (synonym.getSynonymRelations().isEmpty()){\r
+                TaxonNameBase<?,?> name = synonym.getName();\r
+                synonym.setName(null);\r
+                uuid = dao.delete(synonym);\r
+\r
+                //remove name if possible (and required)\r
+                if (name != null && config.isDeleteNameIfPossible()){\r
+\r
+                        nameService.delete(name, config.getNameDeletionConfig());\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
+            }else {\r
+               result.setError();\r
+               result.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));\r
+                return result;\r
             }\r
+\r
+            return result;\r
+        }else{\r
+               List<Exception> exceptions = new ArrayList<Exception>();\r
+               for (String message :messages){\r
+                       exceptions.add(new ReferencedObjectUndeletableException(message));\r
+               }\r
+               result.setError();\r
+               result.addExceptions(exceptions);\r
+            return result;\r
         }\r
+\r
+\r
     }\r
 \r
 \r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)\r
      */\r
+    @Override\r
     public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {\r
 \r
         return this.dao.findIdenticalNamesNew(propertyPath);\r
@@ -865,6 +1416,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
      */\r
+    @Override\r
     public String getPhylumName(TaxonNameBase name){\r
         return this.dao.getPhylumName(name);\r
     }\r
@@ -872,6 +1424,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)\r
      */\r
+    @Override\r
     public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {\r
         return dao.deleteSynonymRelationships(syn, taxon);\r
     }\r
@@ -879,6 +1432,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)\r
      */\r
+    @Override\r
     public long deleteSynonymRelationships(Synonym syn) {\r
         return dao.deleteSynonymRelationships(syn, null);\r
     }\r
@@ -887,6 +1441,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     /* (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
+    @Override\r
     public List<SynonymRelationship> listSynonymRelationships(\r
             TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,\r
             List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {\r
@@ -990,6 +1545,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
         } catch (Exception e){\r
             logger.error(e);\r
+            e.printStackTrace();\r
         }\r
 \r
         return bestCandidate;\r
@@ -1108,98 +1664,548 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
             }\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
+        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
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     */\r
+    @Override\r
+    public Pager<SearchResult<TaxonBase>> findByFullText(\r
+            Class<? extends TaxonBase> clazz, String queryString,\r
+            Classification classification, List<Language> languages,\r
+            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+\r
+        LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments, null);\r
+\r
+        // --- execute search\r
+        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+        idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+        // ---  initialize taxa, thighlight matches ....\r
+        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+    }\r
+\r
+    @Override\r
+    public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTermBase<?>> statusFilter,\r
+            Classification classification,\r
+            Integer pageSize, Integer pageNumber,\r
+            List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, ParseException {\r
+\r
+        LuceneSearch luceneSearch = prepareByDistributionSearch(areaFilter, statusFilter, classification);\r
+\r
+        // --- execute search\r
+        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+        idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+        // ---  initialize taxa, thighlight matches ....\r
+        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+    }\r
+\r
+    /**\r
+     * @param clazz\r
+     * @param queryString\r
+     * @param classification\r
+     * @param languages\r
+     * @param highlightFragments\r
+     * @param sortFields TODO\r
+     * @param directorySelectClass\r
+     * @return\r
+     */\r
+    protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,\r
+            boolean highlightFragments, SortField[] sortFields) {\r
+        BooleanQuery finalQuery = new BooleanQuery();\r
+        BooleanQuery textQuery = new BooleanQuery();\r
+\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
+        QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);\r
+\r
+        if(sortFields == null){\r
+            sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};\r
+        }\r
+        luceneSearch.setSortFields(sortFields);\r
+\r
+        // ---- search criteria\r
+        luceneSearch.setCdmTypRestriction(clazz);\r
+\r
+        textQuery.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+        textQuery.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);\r
+\r
+        finalQuery.add(textQuery, Occur.MUST);\r
+\r
+        if(classification != null){\r
+            finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+        }\r
+        luceneSearch.setQuery(finalQuery);\r
+\r
+        if(highlightFragments){\r
+            luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
+        }\r
+        return luceneSearch;\r
+    }\r
+\r
+    /**\r
+     * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively\r
+     * the BlockJoinQuery could be used. The latter might be more memory save but has the\r
+     * drawback of requiring to do the join an indexing time.\r
+     * see  http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.\r
+     *\r
+     * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:\r
+     * <ul>\r
+     * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --&gt; Taxon.id </li>\r
+     * <li>inverse: {@link Direction.relatedFrom}:  TaxonRelationShip.relatedFrom.id --&gt; Taxon.id </li>\r
+     * <ul>\r
+     * @param queryString\r
+     * @param classification\r
+     * @param languages\r
+     * @param highlightFragments\r
+     * @param sortFields TODO\r
+     *\r
+     * @return\r
+     * @throws IOException\r
+     */\r
+    protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List<Language> languages,\r
+            boolean highlightFragments, SortField[] sortFields) throws IOException {\r
+\r
+        String fromField;\r
+        String queryTermField;\r
+        String toField = "id"; // TaxonBase.uuid\r
+\r
+        if(edge.isBidirectional()){\r
+            throw new RuntimeException("Bidirectional joining not supported!");\r
+        }\r
+        if(edge.isEvers()){\r
+            fromField = "relatedFrom.id";\r
+            queryTermField = "relatedFrom.titleCache";\r
+        } else if(edge.isInvers()) {\r
+            fromField = "relatedTo.id";\r
+            queryTermField = "relatedTo.titleCache";\r
+        } else {\r
+            throw new RuntimeException("Invalid direction: " + edge.getDirections());\r
+        }\r
+\r
+        BooleanQuery finalQuery = new BooleanQuery();\r
+\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
+        QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);\r
+\r
+        BooleanQuery joinFromQuery = new BooleanQuery();\r
+        joinFromQuery.add(taxonBaseQueryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);\r
+        joinFromQuery.add(taxonBaseQueryFactory.newEntityIdQuery("type.id", edge.getTaxonRelationshipType()), Occur.MUST);\r
+        Query joinQuery = taxonBaseQueryFactory.newJoinQuery(fromField, toField, joinFromQuery, TaxonRelationship.class);\r
+\r
+        if(sortFields == null){\r
+            sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};\r
+        }\r
+        luceneSearch.setSortFields(sortFields);\r
+\r
+        finalQuery.add(joinQuery, Occur.MUST);\r
+\r
+        if(classification != null){\r
+            finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+        }\r
+        luceneSearch.setQuery(finalQuery);\r
+\r
+        if(highlightFragments){\r
+            luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
         }\r
-        return result;\r
+        return luceneSearch;\r
     }\r
 \r
-    /* (non-Javadoc)\r
-     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()\r
-     */\r
-    @Override\r
-    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {\r
-        return dao.getUuidAndTitleCacheTaxon();\r
-    }\r
 \r
-    /* (non-Javadoc)\r
-     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()\r
-     */\r
-    @Override\r
-    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {\r
-        return dao.getUuidAndTitleCacheSynonym();\r
-    }\r
+\r
 \r
     /* (non-Javadoc)\r
-     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNamesByFullText(java.util.EnumSet, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.Set, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.Map)\r
      */\r
     @Override\r
-    public Pager<SearchResult<TaxonBase>> findByFullText(\r
-            Class<? extends TaxonBase> clazz, String queryString,\r
-            Classification classification, List<Language> languages,\r
-            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+    public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(\r
+            EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,\r
+            Set<NamedArea> namedAreas, Set<PresenceAbsenceTermBase<?>> distributionStatus, List<Language> languages,\r
+            boolean highlightFragments, Integer pageSize,\r
+            Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)\r
+            throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
+\r
+        // FIXME: allow taxonomic ordering\r
+        //  hql equivalent:  order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";\r
+        // this require building a special sort column by a special classBridge\r
+        if(highlightFragments){\r
+            logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +\r
+                    "currently not fully supported by this method and thus " +\r
+                    "may not work with common names and misapplied names.");\r
+        }\r
 \r
+        // convert sets to lists\r
+        List<NamedArea> namedAreaList = null;\r
+        List<PresenceAbsenceTermBase<?>>distributionStatusList = null;\r
+        if(namedAreas != null){\r
+            namedAreaList = new ArrayList<NamedArea>(namedAreas.size());\r
+            namedAreaList.addAll(namedAreas);\r
+        }\r
+        if(distributionStatus != null){\r
+            distributionStatusList = new ArrayList<PresenceAbsenceTermBase<?>>(distributionStatus.size());\r
+            distributionStatusList.addAll(distributionStatus);\r
+        }\r
 \r
-        LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments);\r
+        // set default if parameter is null\r
+        if(searchModes == null){\r
+            searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);\r
+        }\r
+\r
+        // set sort order and thus override any sort orders which may have been\r
+        // defindes by prepare*Search methods\r
+        if(orderHints == null){\r
+            orderHints = OrderHint.NOMENCLATURAL_SORT_ORDER;\r
+        }\r
+        SortField[] sortFields = new SortField[orderHints.size()];\r
+        int i = 0;\r
+        for(OrderHint oh : orderHints){\r
+            sortFields[i++] = oh.toSortField();\r
+        }\r
+//        SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};\r
+//        SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};\r
 \r
-        // --- execute search\r
-        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
 \r
+        boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;\r
+\r
+        List<LuceneSearch> luceneSearches = new ArrayList<LuceneSearch>();\r
         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
-        idFieldMap.put(CdmBaseType.TAXON, "id");\r
 \r
-        // ---  initialize taxa, thighlight matches ....\r
-        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+        /*\r
+          ======== filtering by distribution , HOWTO ========\r
+\r
+           - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html\r
+           - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter\r
+          add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html\r
+          which will be put into a FilteredQuersy  in the end ?\r
+\r
+\r
+          3. how does it work in spatial?\r
+          see\r
+           - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html\r
+           - http://www.infoq.com/articles/LuceneSpatialSupport\r
+           - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html\r
+          ------------------------------------------------------------------------\r
+\r
+          filter strategies:\r
+          A) use a separate distribution filter per index sub-query/search:\r
+           - byTaxonSyonym (query TaxaonBase):\r
+               use a join area filter (Distribution -> TaxonBase)\r
+           - byCommonName (query DescriptionElementBase): use an area filter on\r
+               DescriptionElementBase !!! PROBLEM !!!\r
+               This cannot work since the distributions are different entities than the\r
+               common names and thus these are different lucene documents.\r
+           - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):\r
+               use a join area filter (Distribution -> TaxonBase)\r
+\r
+          B) use a common distribution filter for all index sub-query/searches:\r
+           - use a common join area filter (Distribution -> TaxonBase)\r
+           - also implement the byCommonName as join query (CommonName -> TaxonBase)\r
+           PROBLEM in this case: we are losing the fragment highlighting for the\r
+           common names, since the returned documents are always TaxonBases\r
+        */\r
+\r
+        /* The QueryFactory for creating filter queries on Distributions should\r
+         * The query factory used for the common names query cannot be reused\r
+         * for this case, since we want to only record the text fields which are\r
+         * actually used in the primary query\r
+         */\r
+        QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);\r
+\r
+        BooleanFilter multiIndexByAreaFilter = new BooleanFilter();\r
+\r
+\r
+        // search for taxa or synonyms\r
+        if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+            Class taxonBaseSubclass = TaxonBase.class;\r
+            if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+                taxonBaseSubclass = Taxon.class;\r
+            } else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+                taxonBaseSubclass = Synonym.class;\r
+            }\r
+            luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments, sortFields));\r
+            idFieldMap.put(CdmBaseType.TAXON, "id");\r
+            /* A) does not work!!!!\r
+            if(addDistributionFilter){\r
+                // in this case we need a filter which uses a join query\r
+                // to get the TaxonBase documents for the DescriptionElementBase documents\r
+                // which are matching the areas in question\r
+                Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+                        namedAreaList,\r
+                        distributionStatusList,\r
+                        distributionFilterQueryFactory\r
+                        );\r
+                multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+            }\r
+            */\r
+            if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+                // add additional area filter for synonyms\r
+                String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+                String toField = "accTaxon.id"; // id in TaxonBase index\r
+\r
+                BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+\r
+                Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+                multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+\r
+            }\r
+        }\r
+\r
+        // search by CommonTaxonName\r
+        if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {\r
+            // B)\r
+            QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+            Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(\r
+                    "inDescription.taxon.id",\r
+                    "id",\r
+                    QueryFactory.addTypeRestriction(\r
+                                createByDescriptionElementFullTextQuery(queryString, classification, null, languages, descriptionElementQueryFactory)\r
+                                , CommonTaxonName.class\r
+                                ),\r
+                    CommonTaxonName.class);\r
+            logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());\r
+            LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+            byCommonNameSearch.setCdmTypRestriction(Taxon.class);\r
+            byCommonNameSearch.setQuery(byCommonNameJoinQuery);\r
+            byCommonNameSearch.setSortFields(sortFields);\r
+            idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+            luceneSearches.add(byCommonNameSearch);\r
+\r
+            /* A) does not work!!!!\r
+            luceneSearches.add(\r
+                    prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,\r
+                            queryString, classification, null, languages, highlightFragments)\r
+                        );\r
+            idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+            if(addDistributionFilter){\r
+                // in this case we are able to use DescriptionElementBase documents\r
+                // which are matching the areas in question directly\r
+                BooleanQuery byDistributionQuery = createByDistributionQuery(\r
+                        namedAreaList,\r
+                        distributionStatusList,\r
+                        distributionFilterQueryFactory\r
+                        );\r
+                multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);\r
+            } */\r
+        }\r
+\r
+        // search by misapplied names\r
+        if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)) {\r
+            // NOTE:\r
+            // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()\r
+            // which allows doing query time joins\r
+            // finds the misapplied name (Taxon B) which is an misapplication for\r
+            // a related Taxon A.\r
+            //\r
+            luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(\r
+                    new TaxonRelationshipEdge(TaxonRelationshipType.MISAPPLIED_NAME_FOR(), Direction.relatedTo),\r
+                    queryString, classification, languages, highlightFragments, sortFields));\r
+            idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+            if(addDistributionFilter){\r
+                String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+\r
+                /*\r
+                 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.\r
+                 * Maybe this is a but in java itself java.\r
+                 *\r
+                 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()\r
+                 * directly:\r
+                 *\r
+                 *    String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";\r
+                 *\r
+                 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query\r
+                 * will execute as expected:\r
+                 *\r
+                 *    String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+                 *    String toField = "relation." + misappliedNameForUuid +".to.id";\r
+                 *\r
+                 * Comparing both strings by the String.equals method returns true, so both String are identical.\r
+                 *\r
+                 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be\r
+                 * dependent from a specific jvm (openjdk6  6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2,  oracle jdk1.7.0_25 tested)\r
+                 * The bug is persistent after a reboot of the development computer.\r
+                 */\r
+//                String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+//                String toField = "relation." + misappliedNameForUuid +".to.id";\r
+                String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";\r
+//                System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");\r
+//                System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");\r
+\r
+                BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+                Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+                QueryWrapperFilter filter = new QueryWrapperFilter(taxonAreaJoinQuery);\r
+\r
+//                debug code for bug described above\r
+                DocIdSet filterMatchSet = filter.getDocIdSet(luceneIndexToolProvider.getIndexReaderFor(Taxon.class));\r
+//                System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));\r
+\r
+                multiIndexByAreaFilter.add(filter, Occur.SHOULD);\r
+            }\r
+        }\r
+\r
+        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,\r
+                luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));\r
+\r
+\r
+        if(addDistributionFilter){\r
+\r
+            // B)\r
+            // in this case we need a filter which uses a join query\r
+            // to get the TaxonBase documents for the DescriptionElementBase documents\r
+            // which are matching the areas in question\r
+            //\r
+            // for toTaxa, doByCommonName\r
+            Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+                    namedAreaList,\r
+                    distributionStatusList,\r
+                    distributionFilterQueryFactory\r
+                    );\r
+            multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+        }\r
+\r
+        if (addDistributionFilter){\r
+            multiSearch.setFilter(multiIndexByAreaFilter);\r
+        }\r
+\r
+\r
+        // --- execute search\r
+        TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+        // --- initialize taxa, highlight matches ....\r
+        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+\r
         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
-                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+                topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
 \r
-        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0;\r
+        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
     }\r
 \r
     /**\r
-     * @param clazz\r
-     * @param queryString\r
+     * @param namedAreaList at least one area must be in the list\r
+     * @param distributionStatusList optional\r
+     * @return\r
+     * @throws IOException\r
+     */\r
+    protected Query createByDistributionJoinQuery(\r
+            List<NamedArea> namedAreaList,\r
+            List<PresenceAbsenceTermBase<?>> distributionStatusList,\r
+            QueryFactory queryFactory\r
+            ) throws IOException {\r
+\r
+        String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+        String toField = "id"; // id in TaxonBase index\r
+\r
+        BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);\r
+\r
+        Query taxonAreaJoinQuery = queryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+\r
+        return taxonAreaJoinQuery;\r
+    }\r
+\r
+    /**\r
+     * @param namedAreaList\r
+     * @param distributionStatusList\r
+     * @param queryFactory\r
+     * @return\r
+     */\r
+    private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,\r
+            List<PresenceAbsenceTermBase<?>> distributionStatusList, QueryFactory queryFactory) {\r
+        BooleanQuery areaQuery = new BooleanQuery();\r
+        // area field from Distribution\r
+        areaQuery.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);\r
+\r
+        // status field from Distribution\r
+        if(distributionStatusList != null && distributionStatusList.size() > 0){\r
+            areaQuery.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);\r
+        }\r
+\r
+        logger.debug("createByDistributionQuery() query: " + areaQuery.toString());\r
+        return areaQuery;\r
+    }\r
+\r
+    /**\r
+     * This method has been primarily created for testing the area join query but might\r
+     * also be useful in other situations\r
+     *\r
+     * @param namedAreaList\r
+     * @param distributionStatusList\r
      * @param classification\r
-     * @param languages\r
      * @param highlightFragments\r
-     * @param directorySelectClass\r
      * @return\r
+     * @throws IOException\r
      */\r
-    protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,\r
-            boolean highlightFragments) {\r
+    protected LuceneSearch prepareByDistributionSearch(\r
+            List<NamedArea> namedAreaList, List<PresenceAbsenceTermBase<?>> distributionStatusList,\r
+            Classification classification) throws IOException {\r
+\r
         BooleanQuery finalQuery = new BooleanQuery();\r
-        BooleanQuery textQuery = new BooleanQuery();\r
 \r
-        LuceneSearch luceneSearch = new LuceneSearch(getSession(), TaxonBase.class);\r
-        QueryFactory queryFactory = new QueryFactory(luceneSearch);\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
 \r
-        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", false)};\r
+        // FIXME is this query factory using the wrong type?\r
+        QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);\r
+\r
+        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
         luceneSearch.setSortFields(sortFields);\r
 \r
-        // ---- search criteria\r
-        luceneSearch.setClazz(clazz);\r
 \r
-        textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
-        textQuery.add(queryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);\r
+        Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory);\r
 \r
-        finalQuery.add(textQuery, Occur.MUST);\r
+        finalQuery.add(byAreaQuery, Occur.MUST);\r
 \r
         if(classification != null){\r
-            finalQuery.add(queryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+            finalQuery.add(taxonQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
         }\r
+\r
+        logger.info("prepareByAreaSearch() query: " + finalQuery.toString());\r
         luceneSearch.setQuery(finalQuery);\r
 \r
-        if(highlightFragments){\r
-            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-        }\r
         return luceneSearch;\r
     }\r
 \r
 \r
+\r
     /* (non-Javadoc)\r
      * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
      */\r
@@ -1230,14 +2236,15 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     }\r
 \r
 \r
+    @Override\r
     public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,\r
             Classification classification, List<Language> languages, boolean highlightFragments,\r
-            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
 \r
         LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
-        LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);\r
+        LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null);\r
 \r
-        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\r
+        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\r
 \r
         // --- execute search\r
         TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
@@ -1252,7 +2259,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
                 topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
 \r
-        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0;\r
+        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
 \r
     }\r
@@ -1268,74 +2275,91 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * @param directorySelectClass\r
      * @return\r
      */\r
-    protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Feature> features,\r
+    protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,\r
+            String queryString, Classification classification, List<Feature> features,\r
             List<Language> languages, boolean highlightFragments) {\r
-        BooleanQuery finalQuery = new BooleanQuery();\r
-        BooleanQuery textQuery = new BooleanQuery();\r
 \r
-        LuceneSearch luceneSearch = new LuceneSearch(getSession(), DescriptionElementBase.class);\r
-        QueryFactory queryFactory = new QueryFactory(luceneSearch);\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);\r
+        QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+\r
+        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.STRING, false)};\r
+\r
+        BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, features,\r
+                languages, descriptionElementQueryFactory);\r
 \r
-        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", false)};\r
         luceneSearch.setSortFields(sortFields);\r
+        luceneSearch.setCdmTypRestriction(clazz);\r
+        luceneSearch.setQuery(finalQuery);\r
+        if(highlightFragments){\r
+            luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());\r
+        }\r
 \r
-        // ---- search criteria\r
-        luceneSearch.setClazz(clazz);\r
-        textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+        return luceneSearch;\r
+    }\r
+\r
+    /**\r
+     * @param queryString\r
+     * @param classification\r
+     * @param features\r
+     * @param languages\r
+     * @param descriptionElementQueryFactory\r
+     * @return\r
+     */\r
+    private BooleanQuery createByDescriptionElementFullTextQuery(String queryString, Classification classification,\r
+            List<Feature> features, List<Language> languages, QueryFactory descriptionElementQueryFactory) {\r
+        BooleanQuery finalQuery = new BooleanQuery();\r
+        BooleanQuery textQuery = new BooleanQuery();\r
+        textQuery.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
 \r
         // common name\r
         Query nameQuery;\r
         if(languages == null || languages.size() == 0){\r
-            nameQuery = queryFactory.newTermQuery("name", queryString);\r
+            nameQuery = descriptionElementQueryFactory.newTermQuery("name", queryString);\r
         } else {\r
             nameQuery = new BooleanQuery();\r
             BooleanQuery languageSubQuery = new BooleanQuery();\r
             for(Language lang : languages){\r
-                languageSubQuery.add(queryFactory.newTermQuery("language.uuid",  lang.getUuid().toString()), Occur.SHOULD);\r
+                languageSubQuery.add(descriptionElementQueryFactory.newTermQuery("language.uuid",  lang.getUuid().toString(), false), Occur.SHOULD);\r
             }\r
-            ((BooleanQuery) nameQuery).add(queryFactory.newTermQuery("name", queryString), Occur.MUST);\r
+            ((BooleanQuery) nameQuery).add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);\r
             ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);\r
         }\r
         textQuery.add(nameQuery, Occur.SHOULD);\r
 \r
 \r
         // text field from TextData\r
-        textQuery.add(queryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
+        textQuery.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
 \r
         // --- TermBase fields - by representation ----\r
         // state field from CategoricalData\r
-        textQuery.add(queryFactory.newDefinedTermQuery("states.state", queryString, languages), Occur.SHOULD);\r
+        textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);\r
 \r
         // state field from CategoricalData\r
-        textQuery.add(queryFactory.newDefinedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD);\r
+        textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);\r
 \r
         // area field from Distribution\r
-        textQuery.add(queryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
+        textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
 \r
         // status field from Distribution\r
-        textQuery.add(queryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);\r
+        textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);\r
 \r
         finalQuery.add(textQuery, Occur.MUST);\r
         // --- classification ----\r
 \r
         if(classification != null){\r
-            finalQuery.add(queryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);\r
+            finalQuery.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);\r
         }\r
 \r
         // --- IdentifieableEntity fields - by uuid\r
         if(features != null && features.size() > 0 ){\r
-            finalQuery.add(queryFactory.newEntityUuidQuery("feature.uuid", features), Occur.MUST);\r
+            finalQuery.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);\r
         }\r
 \r
         // the description must be associated with a taxon\r
-        finalQuery.add(queryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
-\r
-        luceneSearch.setQuery(finalQuery);\r
+        finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
 \r
-        if(highlightFragments){\r
-            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-        }\r
-        return luceneSearch;\r
+        logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());\r
+        return finalQuery;\r
     }\r
 \r
     /**\r
@@ -1366,6 +2390,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return stringBuilder;\r
     }\r
 \r
+    @Override\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
@@ -1373,8 +2398,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();\r
 \r
 \r
-        UUID uuid= taxon.getName().getUuid();\r
-        ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);\r
+        UUID nameUuid= taxon.getName().getUuid();\r
+        ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);\r
         String epithetOfTaxon = null;\r
         String infragenericEpithetOfTaxon = null;\r
         String infraspecificEpithetOfTaxon = null;\r
@@ -1387,7 +2412,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         }\r
         String genusOfTaxon = taxonName.getGenusOrUninomial();\r
         Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
-         List<String> taxonNames = new ArrayList<String>();\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
@@ -1395,9 +2420,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
             if (node.getClassification().equals(classification)){\r
                 if (!node.isTopmostNode()){\r
-                    TaxonNode parent = (TaxonNode)node.getParent();\r
+                    TaxonNode parent = node.getParent();\r
                     parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);\r
-                    TaxonNameBase parentName =  parent.getTaxon().getName();\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
@@ -1428,7 +2453,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
 \r
                         if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){\r
-                            Set<String> genusNames = new HashSet<String>();\r
+\r
 \r
                             for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
                                 Synonym syn = synonymRelationOfParent.getSynonym();\r
@@ -1734,14 +2759,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         String idInSourceSyn= getIdInSource(syn);\r
 \r
         if (idInSourceParent != null && idInSourceSyn != null) {\r
-            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+            IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
             inferredSynName.addSource(originalSource);\r
-            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+            originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
             potentialCombination.addSource(originalSource);\r
         }\r
 \r
-        inferredSynName.generateTitle();\r
-\r
         return potentialCombination;\r
     }\r
 \r
@@ -1797,28 +2820,29 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
         // Add the original source\r
         if (idInSourceSyn != null && idInSourceTaxon != null) {\r
-            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+            IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                    idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
             inferredGenus.addSource(originalSource);\r
 \r
-            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+            originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                    idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
             inferredSynName.addSource(originalSource);\r
             originalSource = null;\r
 \r
         }else{\r
             logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");\r
-            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+            IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                    idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
             inferredGenus.addSource(originalSource);\r
 \r
-            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+            originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                    idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
             inferredSynName.addSource(originalSource);\r
             originalSource = null;\r
         }\r
 \r
         taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());\r
 \r
-        inferredSynName.generateTitle();\r
-\r
-\r
         return inferredGenus;\r
     }\r
 \r
@@ -1829,7 +2853,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
             TaxonNameBase parentName, TaxonBase syn) {\r
 \r
         Synonym inferredEpithet;\r
-        TaxonNameBase synName;\r
+        TaxonNameBase<?,?> synName;\r
         ZoologicalName inferredSynName;\r
         HibernateProxyHelper.deproxy(syn);\r
 \r
@@ -1837,13 +2861,11 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         String idInSourceSyn = getIdInSource(syn);\r
         String idInSourceTaxon =  getIdInSource(taxon);\r
         // Determine the sourceReference\r
-        Reference sourceReference = syn.getSec();\r
+        Reference<?> sourceReference = syn.getSec();\r
 \r
         if (sourceReference == null){\r
-            logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");\r
-            //TODO:Remove\r
-            System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());\r
-            sourceReference = taxon.getSec();\r
+             logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());\r
+             sourceReference = taxon.getSec();\r
         }\r
 \r
         synName = syn.getName();\r
@@ -1900,12 +2922,14 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         }*/\r
         String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
 \r
-        IdentifiableSource originalSource;\r
-        originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+\r
+        IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
 \r
         inferredEpithet.addSource(originalSource);\r
 \r
-        originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+        originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
 \r
         inferredSynName.addSource(originalSource);\r
 \r
@@ -1913,7 +2937,6 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
         taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());\r
 \r
-        inferredSynName.generateTitle();\r
         return inferredEpithet;\r
     }\r
 \r
@@ -1982,6 +3005,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return citation;\r
     }\r
 \r
+    @Override\r
     public List<Synonym>  createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){\r
         List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
 \r
@@ -1992,7 +3016,285 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return inferredSynonyms;\r
     }\r
 \r
+    @Override\r
+    public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {\r
+\r
+        // TODO quickly implemented, create according dao !!!!\r
+        Set<TaxonNode> nodes = new HashSet<TaxonNode>();\r
+        Set<Classification> classifications = new HashSet<Classification>();\r
+        List<Classification> list = new ArrayList<Classification>();\r
+\r
+        if (taxonBase == null) {\r
+            return list;\r
+        }\r
+\r
+        taxonBase = load(taxonBase.getUuid());\r
+\r
+        if (taxonBase instanceof Taxon) {\r
+            nodes.addAll(((Taxon)taxonBase).getTaxonNodes());\r
+        } else {\r
+            for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {\r
+                nodes.addAll(taxon.getTaxonNodes());\r
+            }\r
+        }\r
+        for (TaxonNode node : nodes) {\r
+            classifications.add(node.getClassification());\r
+        }\r
+        list.addAll(classifications);\r
+        return list;\r
+    }\r
+\r
+    @Override\r
+    public Synonym changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,\r
+            SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {\r
+        // Create new synonym using concept name\r
+                TaxonNameBase<?, ?> synonymName = fromTaxon.getName();\r
+                Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());\r
+\r
+                // Remove concept relation from taxon\r
+                toTaxon.removeTaxon(fromTaxon, oldRelationshipType);\r
+\r
+\r
+\r
+\r
+                // Create a new synonym for the taxon\r
+                SynonymRelationship synonymRelationship;\r
+                if (synonymRelationshipType != null\r
+                        && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){\r
+                    synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null);\r
+                } else{\r
+                    synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName);\r
+                }\r
+\r
+                this.saveOrUpdate(toTaxon);\r
+                //TODO: configurator and classification\r
+                TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();\r
+                config.setDeleteNameIfPossible(false);\r
+                this.deleteTaxon(fromTaxon, config, null);\r
+                return synonymRelationship.getSynonym();\r
+\r
+    }\r
+    @Override\r
+    public List<String> isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){\r
+        List<String> result = new ArrayList<String>();\r
+        Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);\r
+        if (taxonBase instanceof Taxon){\r
+            TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;\r
+            result = isDeletableForTaxon(references, taxonConfig);\r
+        }else{\r
+            SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;\r
+            result = isDeletableForSynonym(references, synonymConfig);\r
+        }\r
+        return result;\r
+    }\r
+\r
+    private List<String> isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){\r
+        String message;\r
+        List<String> result = new ArrayList<String>();\r
+        for (CdmBase ref: references){\r
+            if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase )){\r
+                message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();\r
+                result.add(message);\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+    private List<String> isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){\r
+        String message;\r
+        List<String> result = new ArrayList<String>();\r
+        for (CdmBase ref: references){\r
+            if (!(ref instanceof TaxonNameBase)){\r
+                if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){\r
+                    message = "The Taxon can't be deleted as long as it has synonyms.";\r
+                    result.add(message);\r
+                }\r
+                if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){\r
+                    message = "The Taxon can't be deleted as long as it has factual data.";\r
+                    result.add(message);\r
+                }\r
+\r
+                if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){\r
+                    message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+                    result.add(message);\r
+                }\r
+                if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){\r
+                    if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){\r
+                        message = "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";\r
+                        result.add(message);\r
+                    } else{\r
+                        message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+                        result.add(message);\r
+                    }\r
+                }\r
+                if (ref instanceof PolytomousKeyNode){\r
+                    message = "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";\r
+                    result.add(message);\r
+                }\r
+\r
+                if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){\r
+                   message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+                   result.add(message);\r
+\r
+                }\r
+\r
+\r
+               /* //PolytomousKeyNode\r
+                if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+                    String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";\r
+                    return message;\r
+                }*/\r
+\r
+                //TaxonInteraction\r
+                if (ref.isInstanceOf(TaxonInteraction.class)){\r
+                    message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+                    result.add(message);\r
+                }\r
+\r
+              //TaxonInteraction\r
+                if (ref.isInstanceOf(DeterminationEvent.class)){\r
+                    message = "Taxon can't be deleted as it is used in a determination event";\r
+                    result.add(message);\r
+                }\r
+\r
+            }\r
+\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    @Override\r
+    public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) {\r
+        IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid);\r
+\r
+        //preliminary implementation\r
+\r
+        Set<Taxon> taxa = new HashSet<Taxon>();\r
+        TaxonBase taxonBase = find(taxonUuid);\r
+        if (taxonBase == null){\r
+            return new IncludedTaxaDTO();\r
+        }else if (taxonBase.isInstanceOf(Taxon.class)){\r
+            Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);\r
+            taxa.add(taxon);\r
+        }else if (taxonBase.isInstanceOf(Synonym.class)){\r
+            //TODO partial synonyms ??\r
+            //TODO synonyms in general\r
+            Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);\r
+            taxa.addAll(syn.getAcceptedTaxa());\r
+        }else{\r
+            throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName());\r
+        }\r
+\r
+        Set<Taxon> related = makeRelatedIncluded(taxa, result, config);\r
+        int i = 0;\r
+        while((! related.isEmpty()) && i++ < 100){  //to avoid\r
+             related = makeRelatedIncluded(related, result, config);\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa\r
+     * data structure.\r
+     * @return the set of conceptually related taxa for further use\r
+     */\r
+    /**\r
+     * @param uncheckedTaxa\r
+     * @param existingTaxa\r
+     * @param config\r
+     * @return\r
+     */\r
+    private Set<Taxon> makeRelatedIncluded(Set<Taxon> uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+\r
+        //children\r
+        Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();\r
+        for (Taxon taxon: uncheckedTaxa){\r
+            taxonNodes.addAll(taxon.getTaxonNodes());\r
+        }\r
+\r
+        Set<Taxon> children = new HashSet<Taxon>();\r
+        if (! config.onlyCongruent){\r
+            for (TaxonNode node: taxonNodes){\r
+                List<TaxonNode> childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, false);\r
+                for (TaxonNode child : childNodes){\r
+                    children.add(child.getTaxon());\r
+                }\r
+            }\r
+            children.remove(null);  // just to be on the save side\r
+        }\r
+\r
+        Iterator<Taxon> it = children.iterator();\r
+        while(it.hasNext()){\r
+            UUID uuid = it.next().getUuid();\r
+            if (existingTaxa.contains(uuid)){\r
+                it.remove();\r
+            }else{\r
+                existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+            }\r
+        }\r
+\r
+        //concept relations\r
+        Set<Taxon> uncheckedAndChildren = new HashSet<Taxon>(uncheckedTaxa);\r
+        uncheckedAndChildren.addAll(children);\r
+\r
+        Set<Taxon> relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config);\r
+\r
+\r
+        Set<Taxon> result = new HashSet<Taxon>(relatedTaxa);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.\r
+     * @return the set of these computed taxa\r
+     */\r
+    private Set<Taxon> makeConceptIncludedTaxa(Set<Taxon> unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+        Set<Taxon> result = new HashSet<Taxon>();\r
+\r
+        for (Taxon taxon : unchecked){\r
+            Set<TaxonRelationship> fromRelations = taxon.getRelationsFromThisTaxon();\r
+            Set<TaxonRelationship> toRelations = taxon.getRelationsToThisTaxon();\r
+\r
+            for (TaxonRelationship fromRel : fromRelations){\r
+                if (config.includeDoubtful == false && fromRel.isDoubtful()){\r
+                    continue;\r
+                }\r
+                if (fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO()) ||\r
+                        !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.INCLUDES()) ||\r
+                        !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES())\r
+                        ){\r
+                    result.add(fromRel.getToTaxon());\r
+                }\r
+            }\r
+\r
+            for (TaxonRelationship toRel : toRelations){\r
+                if (config.includeDoubtful == false && toRel.isDoubtful()){\r
+                    continue;\r
+                }\r
+                if (toRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){\r
+                    result.add(toRel.getFromTaxon());\r
+                }\r
+            }\r
+        }\r
 \r
+        Iterator<Taxon> it = result.iterator();\r
+        while(it.hasNext()){\r
+            UUID uuid = it.next().getUuid();\r
+            if (existingTaxa.contains(uuid)){\r
+                it.remove();\r
+            }else{\r
+                existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+    @Override\r
+    public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){\r
+        List<TaxonBase> taxonList = dao.getTaxaByName(true, false, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, 0, config.getPropertyPath());\r
+        return taxonList;\r
+    }\r
 \r
 \r
 }\r