fixed problem with deletion of nodes.
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
index 895b61736ca93317b1e54c136d5b6ce15842d931..ac291728973142b3775f5a7106d01605e2088169 100644 (file)
@@ -15,6 +15,7 @@ import java.util.ArrayList;
 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
@@ -22,27 +23,29 @@ import java.util.UUID;
 \r
 import org.apache.log4j.Logger;\r
 import org.apache.lucene.index.CorruptIndexException;\r
-import org.apache.lucene.index.IndexReader;\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.IndexSearcher;\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.join.JoinUtil;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.stereotype.Service;\r
 import org.springframework.transaction.annotation.Transactional;\r
 \r
 import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;\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.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
@@ -70,9 +73,11 @@ import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
 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
@@ -101,9 +106,9 @@ import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\r
-import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer;\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.ITaxonDao;\r
@@ -136,6 +141,10 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
     @Autowired\r
     private INameService nameService;\r
+    \r
+    @Autowired\r
+    private ITaxonNodeService nodeService;\r
+    \r
 \r
     @Autowired\r
     private ICdmGenericDao genericDao;\r
@@ -152,7 +161,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     @Autowired\r
     private AbstractBeanInitializer beanInitializer;\r
 \r
-    private static IndexSearcher taxonRelationshipSearcher;\r
+    @Autowired\r
+    private ILuceneIndexToolProvider luceneIndexToolProvider;\r
 \r
     /**\r
      * Constructor\r
@@ -265,11 +275,11 @@ 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
-\r
+       \r
         TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();\r
         TaxonNameBase<?,?> synonymName = synonym.getName();\r
         HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();\r
@@ -295,12 +305,12 @@ 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
+                this.deleteSynonym(synonym, acceptedTaxon, new SynonymDeletionConfigurator());\r
 \r
             } catch (Exception e) {\r
                 logger.info("Can't delete old synonym from database");\r
@@ -843,7 +853,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
                 // Collection\r
                 //TODO why may collections have media attached? #\r
                 if (occurrence.isInstanceOf(DerivedUnit.class)) {\r
-                       DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);\r
+                    DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);\r
                     if (derivedUnit.getCollection() != null){\r
                         taxonMedia.addAll(derivedUnit.getCollection().getMedia());\r
                     }\r
@@ -851,18 +861,18 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \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
+                    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
@@ -931,20 +941,11 @@ 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
+    public UUID deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config, Classification classification) throws DataChangeNoRollbackException {\r
         if (config == null){\r
             config = new TaxonDeletionConfigurator();\r
         }\r
 \r
-            //         TaxonNode\r
-            if (! config.isDeleteTaxonNodes()){\r
-                if (taxon.getTaxonNodes().size() > 0){\r
-                    String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";\r
-                    throw new ReferencedObjectUndeletableException(message);\r
-                }\r
-            }\r
-\r
-\r
             //         SynonymRelationShip\r
             if (config.isDeleteSynonymRelations()){\r
                 boolean removeSynonymNameFromHomotypicalGroup = false;\r
@@ -954,7 +955,9 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
                     if (config.isDeleteSynonymsIfPossible()){\r
                         //TODO which value\r
                         boolean newHomotypicGroupIfNeeded = true;\r
-                        deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);\r
+                        SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();\r
+                        \r
+                        deleteSynonym(synonym, taxon, synConfig);\r
                     }else{\r
                         deleteSynonymRelationships(synonym, taxon);\r
                     }\r
@@ -967,73 +970,242 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
                     String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";\r
                     throw new ReferencedObjectUndeletableException(message);\r
                 }\r
+            } else{\r
+               for (TaxonRelationship taxRel: taxon.getTaxonRelations()){\r
+                           \r
+                               \r
+                               \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
 \r
+            \r
+                   \r
             //         TaxonDescription\r
+            if (config.isDeleteDescriptions()){\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.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
-                            }\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
+                        descriptionService.delete(desc);\r
+                        taxon.removeDescription(desc);\r
+                    }\r
+            }\r
 \r
 \r
-                //check references with only reverse mapping\r
-            Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);\r
-            for (CdmBase referencingObject : referencingObjects){\r
-                //IIdentificationKeys (Media, Polytomous, MultiAccess)\r
-                if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){\r
-                    String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
-                    message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());\r
-                    throw new ReferencedObjectUndeletableException(message);\r
-                }\r
-\r
-\r
-                //PolytomousKeyNode\r
-                if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
-                    String message = "Taxon can't be deleted as it is used in polytomous key node";\r
-                    throw new ReferencedObjectUndeletableException(message);\r
-                }\r
-\r
-                //TaxonInteraction\r
-                if (referencingObject.isInstanceOf(TaxonInteraction.class)){\r
-                    String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+            //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
+                               } else {\r
+                                       message = "Taxon is not used in defined classification";\r
+                                       throw new DataChangeNoRollbackException(message);\r
+                               }\r
+                       } else if (config.isDeleteInAllClassifications()){\r
+                               List<TaxonNode> nodesList = new ArrayList<TaxonNode>();\r
+                               nodesList.addAll(taxon.getTaxonNodes());\r
+                               \r
+                                       for (TaxonNode taxonNode: nodesList){\r
+                                               if(deleteChildren){\r
+                                                       Object[] childNodes = taxonNode.getChildNodes().toArray();\r
+                                                       for (Object childNode: childNodes){\r
+                                                               TaxonNode childNodeCast = (TaxonNode) childNode;\r
+                                                               deleteTaxon(childNodeCast.getTaxon(), config, classification);\r
+                                                               \r
+                                                       }\r
+                                                       \r
+                                                       /*for (TaxonNode childNode: taxonNode.getChildNodes()){\r
+                                                               deleteTaxon(childNode.getTaxon(), config, classification);\r
+                                                               \r
+                                                       }*/\r
+                                                       //taxon.removeTaxonNode(taxonNode);\r
+                                               } else{\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
+                                                       //taxon.removeTaxonNode(taxonNode);\r
+                                               }\r
+                                       }\r
+                               \r
+                               \r
+                               \r
+                               nodeService.deleteTaxonNodes(nodesList);\r
+                               \r
+                       }\r
+                       if (!success){\r
+                                message = "The taxon node could not be deleted.";\r
+                               throw new DataChangeNoRollbackException(message);\r
+                       }\r
+               }\r
             }\r
-\r
-\r
             //TaxonNameBase\r
             if (config.isDeleteNameIfPossible()){\r
                 try {\r
-                    nameService.delete(taxon.getName(), config.getNameDeletionConfig());\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){\r
+                               taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);\r
+                               name.removeTaxonBase(taxon);\r
+                           nameService.save(name);\r
+                               nameService.delete(name, config.getNameDeletionConfig());\r
+                       }\r
                 } catch (ReferencedObjectUndeletableException e) {\r
                     //do nothing\r
                     if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}\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
+           \r
+\r
+            if (taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0){\r
+               dao.delete(taxon);\r
+               return taxon.getUuid();\r
+            } else{\r
+               message = "Taxon can't be deleted as it is used in another Taxonnode";\r
+               throw new ReferencedObjectUndeletableException(message);\r
+            }\r
+            \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
+        referencingObjects = null;\r
+        return null;\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
     /* (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
+       public void deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {\r
+       deleteSynonym(synonym, null, config);\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, SynonymDeletionConfigurator config) {\r
         if (synonym == null){\r
             return;\r
         }\r
+        if (config == null){\r
+               config = new SynonymDeletionConfigurator();\r
+        }\r
         synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);\r
 \r
         //remove synonymRelationship\r
@@ -1045,23 +1217,26 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         }\r
         for (Taxon relatedTaxon : taxonSet){\r
 //                     dao.deleteSynonymRelationships(synonym, relatedTaxon);\r
-            relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);\r
+            relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded());\r
         }\r
         this.saveOrUpdate(synonym);\r
 \r
         //TODO remove name from homotypical group?\r
 \r
         //remove synonym (if necessary)\r
+        \r
+        \r
         if (synonym.getSynonymRelations().isEmpty()){\r
             TaxonNameBase<?,?> name = synonym.getName();\r
             synonym.setName(null);\r
             dao.delete(synonym);\r
 \r
             //remove name if possible (and required)\r
-            if (name != null && removeNameIfPossible){\r
+            if (name != null && config.isDeleteNameIfPossible()){\r
                 try{\r
-                    nameService.delete(name, new NameDeletionConfigurator());\r
-                }catch (DataChangeNoRollbackException ex){\r
+                    nameService.delete(name, config.getNameDeletionConfig());\r
+                }catch (ReferencedObjectUndeletableException ex){\r
+                       System.err.println("Name wasn't deleted as it is referenced");\r
                     if (logger.isDebugEnabled()) {\r
                         logger.debug("Name wasn't deleted as it is referenced");\r
                     }\r
@@ -1383,6 +1558,29 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         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
@@ -1397,27 +1595,27 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         BooleanQuery finalQuery = new BooleanQuery();\r
         BooleanQuery textQuery = new BooleanQuery();\r
 \r
-        LuceneSearch luceneSearch = new LuceneSearch(getSession(), GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
-        QueryFactory queryFactory = new QueryFactory(luceneSearch);\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);\r
+        QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.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
+        luceneSearch.setCdmTypRestriction(clazz);\r
 \r
-        textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
-        textQuery.add(queryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);\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(queryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+            finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
         }\r
         luceneSearch.setQuery(finalQuery);\r
 \r
         if(highlightFragments){\r
-            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
+            luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
         }\r
         return luceneSearch;\r
     }\r
@@ -1439,11 +1637,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
      * @param languages\r
      * @param highlightFragments\r
      * @return\r
+     * @throws IOException\r
      */\r
     protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List<Language> languages,\r
-            boolean highlightFragments) {\r
+            boolean highlightFragments) throws IOException {\r
 \r
-        String idField;\r
+        String fromField;\r
         String queryTermField;\r
         String toField = "id"; // TaxonBase.uuid\r
 \r
@@ -1451,36 +1650,24 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
             throw new RuntimeException("Bidirectional joining not supported!");\r
         }\r
         if(edge.isEvers()){\r
-            idField = "relatedFrom.id";\r
+            fromField = "relatedFrom.id";\r
             queryTermField = "relatedFrom.titleCache";\r
         } else if(edge.isInvers()) {\r
-            idField = "relatedTo.id";\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
-        Query joinQuery = null;\r
-\r
-        LuceneSearch luceneSearch = new LuceneSearch(getSession(), TaxonBase.class);\r
-        QueryFactory queryFactory = new QueryFactory(luceneSearch);\r
-\r
-        joinFromQuery.add(queryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);\r
-        joinFromQuery.add(queryFactory.newEntityIdQuery("type.id", edge.getTaxonRelationshipType()), Occur.MUST);\r
-        try {\r
-            // TODO move into QueryFactory if possible\r
-            if(taxonRelationshipSearcher == null){\r
-                IndexReader taxonRelationshipReader = luceneSearch.getIndexReaderFor(TaxonRelationship.class);\r
-                taxonRelationshipSearcher = new IndexSearcher(taxonRelationshipReader);\r
-                taxonRelationshipSearcher.setDefaultFieldSortScoring(true, true);\r
-            }\r
-            joinQuery = JoinUtil.createJoinQuery(idField, toField, joinFromQuery, taxonRelationshipSearcher);\r
-            // end of possible move\r
-        } catch (IOException e) {\r
-            logger.error(e);\r
-        }\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
         SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};\r
         luceneSearch.setSortFields(sortFields);\r
@@ -1488,12 +1675,12 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         finalQuery.add(joinQuery, Occur.MUST);\r
 \r
         if(classification != null){\r
-            finalQuery.add(queryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+            finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
         }\r
         luceneSearch.setQuery(finalQuery);\r
 \r
         if(highlightFragments){\r
-            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
+            luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());\r
         }\r
         return luceneSearch;\r
     }\r
@@ -1507,19 +1694,87 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
     @Override\r
     public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(\r
             EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,\r
-            Set<NamedArea> namedAreas, List<Language> languages, boolean highlightFragments, Integer pageSize,\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
         // set default if parameter is null\r
         if(searchModes == null){\r
             searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);\r
         }\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
 \r
-\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
@@ -1529,25 +1784,148 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
             }\r
             luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments));\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
-            luceneSearches.add(prepareByDescriptionElementFullTextSearch(CommonTaxonName.class, queryString, classification, null, languages, highlightFragments));\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
+            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
-        }\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));\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
-        // TODO implement area filter\r
+        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,\r
+                luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));\r
+\r
 \r
-        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));\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
         // --- execute search\r
         TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
 \r
@@ -1562,6 +1940,91 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
     }\r
 \r
+    /**\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 highlightFragments\r
+     * @return\r
+     * @throws IOException\r
+     */\r
+    protected LuceneSearch prepareByDistributionSearch(\r
+            List<NamedArea> namedAreaList, List<PresenceAbsenceTermBase<?>> distributionStatusList,\r
+            Classification classification) throws IOException {\r
+\r
+        BooleanQuery finalQuery = new BooleanQuery();\r
+\r
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+\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
+\r
+        Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory);\r
+\r
+        finalQuery.add(byAreaQuery, Occur.MUST);\r
+\r
+        if(classification != null){\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
+        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
@@ -1600,7 +2063,7 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
         LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);\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
@@ -1631,75 +2094,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(), GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, 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
         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(), false), 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("stateData.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("stateData.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
+        finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
 \r
         logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());\r
-        luceneSearch.setQuery(finalQuery);\r
-\r
-        if(highlightFragments){\r
-            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-        }\r
-        return luceneSearch;\r
+        return finalQuery;\r
     }\r
 \r
     /**\r
@@ -2162,23 +2641,23 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
 \r
         // Add the original source\r
         if (idInSourceSyn != null && idInSourceTaxon != null) {\r
-            IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, \r
-                       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(OriginalSourceType.Transformation, \r
-                       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(OriginalSourceType.Transformation, \r
-                       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(OriginalSourceType.Transformation, \r
-                       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
@@ -2209,10 +2688,8 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         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
@@ -2270,13 +2747,13 @@ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDa
         String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
 \r
 \r
-        IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, \r
-                       taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+        IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+                taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
 \r
         inferredEpithet.addSource(originalSource);\r
 \r
-        originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, \r
-                       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