resolving conflicts after merge of ticket_4716 into develop
authorAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Mon, 21 Dec 2015 15:50:54 +0000 (16:50 +0100)
committerAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Mon, 21 Dec 2015 15:50:54 +0000 (16:50 +0100)
23 files changed:
1  2 
cdmlib-commons/pom.xml
cdmlib-ext/pom.xml
cdmlib-io/pom.xml
cdmlib-model/pom.xml
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/OrderedTermVocabulary.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/name/Rank.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/TaxonRelationship.java
cdmlib-persistence/pom.xml
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/hibernate/common/DefinedTermDaoImpl.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/hibernate/description/DescriptionDaoImpl.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/CdmListenerIntegrator.java
cdmlib-print/pom.xml
cdmlib-remote-webapp/pom.xml
cdmlib-remote/pom.xml
cdmlib-remote/src/main/java/eu/etaxonomy/cdm/remote/controller/DescriptionListPortalController.java
cdmlib-services/pom.xml
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/IOccurrenceService.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/NameServiceImpl.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/OccurrenceServiceImpl.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonNodeServiceImpl.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/TaxonServiceImpl.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/DescriptionServiceImplTest.java
pom.xml

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 9d9cfcb9bebdcee7410fca6f6a08c032f01e8e0f,54d6cfec6564c0441fff646fa3fc9db1509e6439..f43981b9f99c1f3999c05ff1ce4546ff4f9e0ece
 -// $Id$\r
 -/**\r
 - * Copyright (C) 2009 EDIT European Distributed Institute of Taxonomy\r
 - * http://www.e-taxonomy.eu\r
 - *\r
 - * The contents of this file are subject to the Mozilla Public License Version\r
 - * 1.1 See LICENSE.TXT at the top of this package for the full license terms.\r
 - */\r
 -\r
 -package eu.etaxonomy.cdm.remote.controller;\r
 -\r
 -import io.swagger.annotations.Api;\r
 -\r
 -import java.util.Arrays;\r
 -import java.util.List;\r
 -\r
 -import org.apache.log4j.Logger;\r
 -import org.springframework.stereotype.Controller;\r
 -import org.springframework.web.bind.annotation.RequestMapping;\r
 -\r
 -/**\r
 - *\r
 - * @author a.kohlbecker\r
 - * @date Jun 25, 2013\r
 - *\r
 - */\r
 -@Controller\r
 -@Api("portal_description")\r
 -@RequestMapping(value = {"/portal/description"})\r
 -public class DescriptionListPortalController extends DescriptionListController {\r
 -\r
 -   public static final Logger logger = Logger.getLogger(DescriptionListPortalController.class);\r
 -\r
 -    protected static final List<String> DESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{\r
 -            "$",\r
 -            "elements.$",\r
 -            "elements.annotations",\r
 -            "elements.markers",\r
 -            "elements.stateData.$",\r
 -            "elements.sources.citation.authorship",\r
 -            "elements.sources.nameUsedInSource",\r
 -            "elements.multilanguageText",\r
 -            "elements.media",\r
 -            "elements.kindOfUnit"\r
 -    });\r
 -\r
 -\r
 -    protected static final List<String> DISTRIBUTION_INFO_INIT_STRATEGY = Arrays.asList(new String []{\r
 -            "sources.citation.authorship.$",\r
 -            "sources.nameUsedInSource",\r
 -            "annotations"\r
 -    });\r
 -\r
 -\r
 -    public DescriptionListPortalController() {\r
 -        super();\r
 -        setInitializationStrategy(DESCRIPTION_INIT_STRATEGY);\r
 -    }\r
 -\r
 -    @Override\r
 -    protected List<String> getDescriptionInfoInitStrategy(){\r
 -        return DISTRIBUTION_INFO_INIT_STRATEGY;\r
 -    }\r
 -\r
 +// $Id$
 +/**
 + * Copyright (C) 2009 EDIT European Distributed Institute of Taxonomy
 + * http://www.e-taxonomy.eu
 + *
 + * The contents of this file are subject to the Mozilla Public License Version
 + * 1.1 See LICENSE.TXT at the top of this package for the full license terms.
 + */
 +
 +package eu.etaxonomy.cdm.remote.controller;
 +
- import java.io.IOException;
++import io.swagger.annotations.Api;
++
 +import java.util.Arrays;
 +import java.util.List;
- import java.util.UUID;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.log4j.Logger;
- import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.WebDataBinder;
- import org.springframework.web.bind.annotation.InitBinder;
 +import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import org.springframework.web.bind.annotation.RequestParam;
- import com.wordnik.swagger.annotations.Api;
- import eu.etaxonomy.cdm.api.service.IDescriptionService;
- import eu.etaxonomy.cdm.api.service.ITaxonService;
- import eu.etaxonomy.cdm.api.service.ITermService;
- import eu.etaxonomy.cdm.api.service.pager.Pager;
- import eu.etaxonomy.cdm.model.description.DescriptionBase;
- import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
- import eu.etaxonomy.cdm.model.description.Feature;
- import eu.etaxonomy.cdm.model.taxon.Taxon;
- import eu.etaxonomy.cdm.persistence.query.MatchMode;
- import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
- import eu.etaxonomy.cdm.remote.editor.DefinedTermBaseList;
- import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;
 +
 +/**
-  * IMPORTANT:
-  *
-  * This controller is mostly a 1:1 copy of the DescriptionListController
-  * and this provides identical end points which only differ in the depth of the
-  * object graphs returned.
-  * An exception is the doAccumulateDistributions() method, which is not repeated
-  * here
 + *
 + * @author a.kohlbecker
 + * @date Jun 25, 2013
 + *
 + */
 +@Controller
 +@Api("portal_description")
 +@RequestMapping(value = {"/portal/description"})
- public class DescriptionListPortalController extends IdentifiableListController<DescriptionBase, IDescriptionService> {
++public class DescriptionListPortalController extends DescriptionListController {
 +
 +   public static final Logger logger = Logger.getLogger(DescriptionListPortalController.class);
 +
-     @Autowired
-     private ITaxonService taxonService;
 +    protected static final List<String> DESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{
 +            "$",
 +            "elements.$",
 +            "elements.annotations",
 +            "elements.markers",
 +            "elements.stateData.$",
 +            "elements.sources.citation.authorship",
 +            "elements.sources.nameUsedInSource",
 +            "elements.multilanguageText",
 +            "elements.media",
 +            "elements.kindOfUnit"
 +    });
 +
-     protected static final List<String> DESCRIPTION_ELEMENT_INIT_STRATEGY = Arrays.asList(new String []{
-             "$",
-             "annotations",
-             "markers",
-             "stateData.$",
-             "statisticalValues.*",
-             "sources.citation.authorship",
++
++    protected static final List<String> DISTRIBUTION_INFO_INIT_STRATEGY = Arrays.asList(new String []{
++            "sources.citation.authorship.$",
 +            "sources.nameUsedInSource",
-             "multilanguageText",
-             "media",
-             "name.$",
-             "name.rank.representations",
-             "name.status.type.representations",
-             "taxon2.name"
++            "annotations"
 +    });
 +
 +
 +    public DescriptionListPortalController() {
 +        super();
 +        setInitializationStrategy(DESCRIPTION_INIT_STRATEGY);
 +    }
 +
-     @Autowired
-     private ITermService termService;
-     /* (non-Javadoc)
-      * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)
-      */
-     @Override
-     @Autowired
-     public void setService(IDescriptionService service) {
-         this.service = service;
-     }
-     @InitBinder
 +    @Override
-     public void initBinder(WebDataBinder binder) {
-         super.initBinder(binder);
-         binder.registerCustomEditor(DefinedTermBaseList.class, new TermBaseListPropertyEditor<Feature>(termService));
++    protected List<String> getDescriptionInfoInitStrategy(){
++        return DISTRIBUTION_INFO_INIT_STRATEGY;
 +    }
-     /**
-     *
-     * @param queryString
-     * @param type
-     * @param pageSize
-     * @param pageNumber
-     * @param matchMode
-     * @param request
-     * @param response
-     * @return
-     * @throws IOException
-     */
-    @RequestMapping(value = "//portal/descriptionElement/find", method = RequestMethod.GET) // mapped as absolute path, see CdmAntPathMatcher
-    public Pager<DescriptionElementBase> doFindDescriptionElements(
-            @RequestParam(value = "query", required = true) String queryString,
-            @RequestParam(value = "type", required = false) Class<? extends DescriptionElementBase> type,
-            @RequestParam(value = "pageSize", required = false) Integer pageSize,
-            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,
-            @RequestParam(value = "matchMode", required = false) MatchMode matchMode,
-            HttpServletRequest request,
-            HttpServletResponse response
-            )
-             throws IOException {
-        logger.info("doFindDescriptionElements : " + request.getRequestURI() + "?" + request.getQueryString() );
-        PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);
-        pagerParams.normalizeAndValidate(response);
-        Pager<DescriptionElementBase> pager = service.searchElements(type, queryString, pageSize, pageNumber, null, getInitializationStrategy());
-        return pager;
-    }
-    /**
-     * Requires the query parameter "descriptionType" to be present
-     *
-     * @param features
-     * @param descriptionType
-     * @param type
-     * @param pageSize
-     * @param pageNumber
-     * @param request
-     * @param response
-     * @return
-     * @throws IOException
-     */
-    @RequestMapping(value = "//portal/descriptionElement/byFeature", method = {RequestMethod.GET, RequestMethod.POST}) // mapped as absolute path, see CdmAntPathMatcher
-    public Pager<DescriptionElementBase> doPageDescriptionElementsByFeature(
-            @RequestParam(value = "features", required = false) DefinedTermBaseList<Feature> features,
-            @RequestParam(value = "descriptionType", required = true) Class<? extends DescriptionBase> descriptionType,
-            @RequestParam(value = "type", required = false) Class<? extends DescriptionElementBase> type,
-            @RequestParam(value = "pageSize", required = false) Integer pageSize,
-            @RequestParam(value = "pageNumber", required = false) Integer pageNumber, HttpServletRequest request,
-            HttpServletResponse response) throws IOException {
-        logger.info("doPageDescriptionElementsByFeature : " + requestPathAndQuery(request));
-        PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);
-        pagerParams.normalizeAndValidate(response);
-        if(features == null){
-            features = new DefinedTermBaseList<Feature>();
-        }
-        Pager pager = service.pageDescriptionElements(null, descriptionType, features.asSet(),
-                type, pagerParams.getPageSize(), pagerParams.getPageIndex(), DESCRIPTION_ELEMENT_INIT_STRATEGY);
-        return pager;
-    }
-    /**
-     * Requires the query parameter "taxon"  to be present
-     *
-     * @param taxon_uuid
-     * @param features
-     * @param type
-     * @param pageSize
-     * @param pageNumber
-     * @param request
-     * @param response
-     * @return
-     * @throws IOException
-     */
-    @RequestMapping(value = "//portal/descriptionElement/byTaxon", method = {RequestMethod.GET, RequestMethod.POST}) // mapped as absolute path, see CdmAntPathMatcher
-    public <T extends DescriptionElementBase> Pager<T> getDescriptionElementsForTaxon(
-            @RequestParam(value = "taxon", required = true) UUID taxon_uuid,
-            @RequestParam(value = "features", required = false) DefinedTermBaseList<Feature> features,
-            @RequestParam(value = "type", required = false) Class<T> type,
-            @RequestParam(value = "pageSize", required = false) Integer pageSize,
-            @RequestParam(value = "pageNumber", required = false) Integer pageNumber, HttpServletRequest request,
-            HttpServletResponse response) throws IOException {
-        logger.info("getDescriptionElementsForTaxon : " + requestPathAndQuery(request));
-        PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);
-        pagerParams.normalizeAndValidate(response);
 +
-        Taxon taxon = null;
-        if( taxon_uuid!= null){
-            try {
-                taxon = (Taxon) taxonService.load(taxon_uuid);
-            } catch (Exception e) {
-                HttpStatusMessage.UUID_NOT_FOUND.send(response);
-            }
-        }
-        //TODO it seems as if the InitializationStrategy is not appropriate here !!!
-        //   see #3728 (DescriptionListPortalController.getDescriptionElementsForTaxon() seems to be using in-appropriate init strategy)
-        if (logger.isDebugEnabled()){logger.debug("get pager ...");}
-        Pager<T> pager = service.pageDescriptionElementsForTaxon(
-                taxon,
-                (features != null ? features.asSet() : null),
-                type,
-                pagerParams.getPageSize(),
-                pagerParams.getPageIndex(),
-                DESCRIPTION_ELEMENT_INIT_STRATEGY
-               );
-        if (logger.isDebugEnabled()){logger.debug("get pager - DONE");}
-        return pager;
-    }
  }
Simple merge
index de59de2c0b441d38d9a9ae8541c5a0182f6fe568,9a3a58e189f7bdaaa8519c45bb10e8904cd1093e..a45e634f8b3c6b1064d6fc2383a360f94230c41e
- // $Id$\r
- /**\r
- * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy\r
- * http://www.e-taxonomy.eu\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version 1.1\r
- * See LICENSE.TXT at the top of this package for the full license terms.\r
- */\r
\r
- package eu.etaxonomy.cdm.api.service;\r
\r
- import java.io.IOException;\r
- import java.util.ArrayList;\r
- import java.util.Collection;\r
- import java.util.HashMap;\r
- import java.util.HashSet;\r
- import java.util.List;\r
- import java.util.Map;\r
- import java.util.Set;\r
- import java.util.UUID;\r
\r
- import org.apache.log4j.Logger;\r
- import org.apache.lucene.index.CorruptIndexException;\r
- import org.apache.lucene.index.Term;\r
- import org.apache.lucene.queryParser.ParseException;\r
- import org.apache.lucene.search.BooleanClause.Occur;\r
- import org.apache.lucene.search.BooleanQuery;\r
- import org.apache.lucene.search.FuzzyLikeThisQuery;\r
- import org.apache.lucene.search.TopDocs;\r
- import org.apache.lucene.search.WildcardQuery;\r
- import org.hibernate.criterion.Criterion;\r
- import org.springframework.beans.factory.annotation.Autowired;\r
- import org.springframework.beans.factory.annotation.Qualifier;\r
- import org.springframework.stereotype.Service;\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.NameDeletionConfigurator;\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.AbstractPagerImpl;\r
- import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
- import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;\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.LuceneSearch;\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.common.monitor.IProgressMonitor;\r
- import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
- import eu.etaxonomy.cdm.model.CdmBaseType;\r
- import eu.etaxonomy.cdm.model.common.CdmBase;\r
- import eu.etaxonomy.cdm.model.common.Language;\r
- import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;\r
- import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
- import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
- import eu.etaxonomy.cdm.model.description.DescriptionElementSource;\r
- import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
- import eu.etaxonomy.cdm.model.name.HybridRelationship;\r
- import eu.etaxonomy.cdm.model.name.HybridRelationshipType;\r
- import eu.etaxonomy.cdm.model.name.NameRelationship;\r
- import eu.etaxonomy.cdm.model.name.NameRelationshipType;\r
- import eu.etaxonomy.cdm.model.name.NameTypeDesignation;\r
- import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;\r
- import eu.etaxonomy.cdm.model.name.NonViralName;\r
- import eu.etaxonomy.cdm.model.name.Rank;\r
- import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;\r
- import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
- import eu.etaxonomy.cdm.model.name.TypeDesignationBase;\r
- import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
- import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;\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.common.IReferencedEntityDao;\r
- import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;\r
- import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;\r
- import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;\r
- import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;\r
- import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;\r
- import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;\r
- import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
- import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
- import eu.etaxonomy.cdm.strategy.cache.TaggedText;\r
- import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;\r
- import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;\r
\r
\r
- @Service\r
- @Transactional(readOnly = true)\r
- public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxonNameDao> implements INameService {\r
-     static private final Logger logger = Logger.getLogger(NameServiceImpl.class);\r
\r
-     @Autowired\r
-     protected ITermVocabularyDao vocabularyDao;\r
-     @Autowired\r
-     protected IOrderedTermVocabularyDao orderedVocabularyDao;\r
-     @Autowired\r
-     @Qualifier("refEntDao")\r
-     protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;\r
-     @Autowired\r
-     private INomenclaturalStatusDao nomStatusDao;\r
-     @Autowired\r
-     private ITypeDesignationDao typeDesignationDao;\r
-     @Autowired\r
-     private IHomotypicalGroupDao homotypicalGroupDao;\r
-     @Autowired\r
-     private ICdmGenericDao genericDao;\r
-     @Autowired\r
-     private ILuceneIndexToolProvider luceneIndexToolProvider;\r
\r
-     /**\r
-      * Constructor\r
-      */\r
-     public NameServiceImpl(){\r
-         if (logger.isDebugEnabled()) { logger.debug("Load NameService Bean"); }\r
-     }\r
\r
- //********************* METHODS ****************************************************************//\r
\r
-     /* (non-Javadoc)\r
-      * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)\r
-      */\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult delete(UUID nameUUID){\r
-         NameDeletionConfigurator config = new NameDeletionConfigurator();\r
-         DeleteResult result = delete(nameUUID, config);\r
-         return result;\r
-     }\r
\r
-     @Override\r
-     public DeleteResult delete(TaxonNameBase name){\r
-         return delete(name.getUuid());\r
-     }\r
\r
-     /* (non-Javadoc)\r
-      * @see eu.etaxonomy.cdm.api.service.INameService#delete(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.api.service.NameDeletionConfigurator)\r
-      */\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {\r
-       DeleteResult result = new DeleteResult();\r
-               TaxonNameBase name = dao.load(nameUUID);\r
\r
-       if (name == null){\r
-               result.setAbort();\r
-             return result;\r
-         }\r
\r
-       try{\r
-               result = this.isDeletable(name, config);\r
-         }catch(Exception e){\r
-               result.addException(e);\r
-               result.setError();\r
-               return result;\r
-         }\r
-         if (result.isOk()){\r
-         //remove references to this name\r
-               removeNameRelationshipsByDeleteConfig(name, config);\r
\r
-            //remove name from homotypical group\r
-             HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();\r
-             if (homotypicalGroup != null){\r
-                 homotypicalGroup.removeTypifiedName(name, false);\r
-             }\r
\r
-              //all type designation relationships are removed as they belong to the name\r
-               deleteTypeDesignation(name, null);\r
-       //              //type designations\r
-       //              if (! name.getTypeDesignations().isEmpty()){\r
-       //                      String message = "Name can't be deleted as it has types. Remove types prior to deletion.";\r
-       //                      throw new ReferrencedObjectUndeletableException(message);\r
-       //              }\r
\r
\r
-               try{\r
-               UUID nameUuid = dao.delete(name);\r
\r
-               }catch(Exception e){\r
-                       result.addException(e);\r
-                       result.setError();\r
-               }\r
-               return result;\r
-         }\r
\r
\r
\r
-         return result;\r
-     }\r
\r
-     /* (non-Javadoc)\r
-      * @see eu.etaxonomy.cdm.api.service.INameService#deleteTypeDesignation(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.model.name.TypeDesignationBase)\r
-      */\r
-     @Override\r
-     public void deleteTypeDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation){\r
-         if (name == null && typeDesignation == null){\r
-             return;\r
-         }else if (name != null && typeDesignation != null){\r
-             removeSingleDesignation(name, typeDesignation);\r
-         }else if (name != null){\r
-             Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());\r
-             for (Object o : designationSet){\r
-                 TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);\r
-                 removeSingleDesignation(name, desig);\r
-             }\r
-         }else if (typeDesignation != null){\r
-             Set<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());\r
-             for (Object o : nameSet){\r
-                 TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);\r
-                 removeSingleDesignation(singleName, typeDesignation);\r
-             }\r
-         }\r
-     }\r
\r
-     /**\r
-      * @param name\r
-      * @param typeDesignation\r
-      */\r
-     private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {\r
-         name.removeTypeDesignation(typeDesignation);\r
-         if (typeDesignation.getTypifiedNames().isEmpty()){\r
-             typeDesignation.removeType();\r
-             typeDesignationDao.delete(typeDesignation);\r
-         }\r
-     }\r
\r
\r
\r
-     /**\r
-      * @param name\r
-      * @param config\r
-      */\r
-     private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {\r
-         try {\r
-             if (config.isRemoveAllNameRelationships()){\r
-                 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());\r
-                 for (NameRelationship rel : rels){\r
-                     name.removeNameRelationship(rel);\r
-                 }\r
-             }else{\r
-                 //relations to this name\r
-                 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());\r
-                 for (NameRelationship rel : rels){\r
-                     if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){\r
-                             name.removeNameRelationship(rel);\r
-                     }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){\r
-                         name.removeNameRelationship(rel);\r
-                     }\r
-                 }\r
-                 //relations from this name\r
-                 rels = getModifiableSet(name.getRelationsFromThisName());\r
-                 for (NameRelationship rel : rels){\r
-                     if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){\r
-                         name.removeNameRelationship(rel);\r
-                     }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){\r
-                         name.removeNameRelationship(rel);\r
-                     }\r
-                 }\r
\r
-             }\r
-         } catch (Exception e) {\r
-             throw new RuntimeException(e);\r
-         }\r
-     }\r
\r
-     /**\r
-      * @param name\r
-      * @return\r
-      */\r
-     private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {\r
-         Set<NameRelationship> rels = new HashSet<NameRelationship>();\r
-         for (NameRelationship rel : relations){\r
-             rels.add(rel);\r
-         }\r
-         return rels;\r
-     }\r
\r
- //********************* METHODS ****************************************************************//\r
\r
-     /**\r
-      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions\r
-      * duplicate of findByName\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public List getNamesByName(String name){\r
-         return super.findCdmObjectsByTitle(name);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name findByName\r
-      */\r
-     @Override\r
-     public List<NonViralName> getNamesByNameCache(String nameCache){\r
-         List result = dao.findByName(nameCache, MatchMode.EXACT, null, null, null, null);\r
-         return result;\r
-     }\r
\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name saveHomotypicalGroups\r
-      *\r
-      * findByTitle\r
-      */\r
-     @Override\r
-     public List<NonViralName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){\r
-         List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);\r
-         return result;\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name saveHomotypicalGroups\r
-      *\r
-      * findByTitle\r
-      */\r
-     @Override\r
-     public List<NonViralName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){\r
-         List result = dao.findByName(nameCache, matchMode, null, null, null ,propertyPaths);\r
-         return result;\r
-     }\r
\r
-     /**\r
-      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions\r
-      * Replace by load(UUID, propertyPaths)\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public NonViralName findNameByUuid(UUID uuid, List<String> propertyPaths){\r
-         return (NonViralName)dao.findByUuid(uuid, null ,propertyPaths);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      */\r
-     @Override\r
-     public List getNamesByName(String name, CdmBase sessionObject){\r
-         return super.findCdmObjectsByTitle(name, sessionObject);\r
-     }\r
\r
-     /**\r
-      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions\r
-      * duplicate of findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths)\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public List findNamesByTitle(String title){\r
-         return super.findCdmObjectsByTitle(title);\r
-     }\r
\r
-     /**\r
-      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions\r
-      * duplicate of findByTitle()\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public List findNamesByTitle(String title, CdmBase sessionObject){\r
-         return super.findCdmObjectsByTitle(title, sessionObject);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name saveHomotypicalGroups\r
-      */\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){\r
-         return homotypicalGroupDao.saveAll(homotypicalGroups);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name saveTypeDesignations\r
-      */\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){\r
-         return typeDesignationDao.saveAll(typeDesignationCollection);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name saveReferencedEntities\r
-      */\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){\r
-         return referencedEntityDao.saveAll(referencedEntityCollection);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name getNames\r
-      */\r
-     public List<TaxonNameBase> getAllNames(int limit, int start){\r
-         return dao.list(limit, start);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name getNomenclaturalStatus\r
-      */\r
-     @Override\r
-     public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){\r
-         return nomStatusDao.list(limit, start);\r
-     }\r
\r
-     /**\r
-      * TODO candidate for harmonization\r
-      * new name getTypeDesignations\r
-      */\r
-     @Override\r
-     public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){\r
-         return typeDesignationDao.getAllTypeDesignations(limit, start);\r
-     }\r
-       /**\r
-      * FIXME Candidate for harmonization\r
-      * homotypicalGroupService.list\r
-      */\r
-     @Override\r
-     public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){\r
-         return homotypicalGroupDao.list(limit, start);\r
-     }\r
\r
-     /**\r
-      * FIXME Candidate for harmonization\r
-      * remove\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public List<RelationshipBase> getAllRelationships(int limit, int start){\r
-         return dao.getAllRelationships(limit, start);\r
-     }\r
\r
\r
\r
-     @Override\r
-     @Autowired\r
-     protected void setDao(ITaxonNameDao dao) {\r
-         this.dao = dao;\r
-     }\r
\r
-     @Override\r
-     public Pager<HybridRelationship> getHybridNames(NonViralName name,        HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-         Integer numberOfResults = dao.countHybridNames(name, type);\r
\r
-         List<HybridRelationship> results = new ArrayList<HybridRelationship>();\r
-         if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again\r
-             results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);\r
-         }\r
\r
-         return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public List<NameRelationship> listNameRelationships(TaxonNameBase name,   Direction direction, NameRelationshipType type, Integer pageSize,\r
-             Integer pageNumber, List<OrderHint> orderHints,   List<String> propertyPaths) {\r
\r
-         Integer numberOfResults = dao.countNameRelationships(name, direction, type);\r
\r
-         List<NameRelationship> results = new ArrayList<NameRelationship>();\r
-         if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again\r
-             results = dao.getNameRelationships(name, direction, type, pageSize,       pageNumber, orderHints, propertyPaths);\r
-         }\r
-         return results;\r
-     }\r
\r
\r
-     protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,\r
-             NonViralName nvn,\r
-             float accuracy,\r
-             int maxNoOfResults,\r
-             List<Language> languages,\r
-             boolean highlightFragments) {\r
-         String similarity = Float.toString(accuracy);\r
-         String searchSuffix = "~" + similarity;\r
\r
\r
-         BooleanQuery finalQuery = new BooleanQuery(false);\r
-         BooleanQuery textQuery = new BooleanQuery(false);\r
\r
-         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);\r
-         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.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.setCdmTypRestriction(clazz);\r
\r
-         FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());\r
-         if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {\r
-             fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);\r
-         } else {\r
-             //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);\r
-             textQuery.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);\r
-         }\r
\r
-         if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){\r
-             fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);\r
-         } else {\r
-             //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);\r
-             textQuery.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);\r
-         }\r
\r
-         if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){\r
-             fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);\r
-         } else {\r
-             //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);\r
-             textQuery.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);\r
-         }\r
\r
-         if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){\r
-             fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);\r
-         } else {\r
-             //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);\r
-             textQuery.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);\r
-         }\r
\r
-         if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){\r
-             fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);\r
-         } else {\r
-             //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);\r
-         }\r
\r
-         textQuery.add(fltq, Occur.MUST);\r
\r
-         finalQuery.add(textQuery, Occur.MUST);\r
\r
-         luceneSearch.setQuery(finalQuery);\r
\r
-         if(highlightFragments){\r
-             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-         }\r
-         return luceneSearch;\r
-     }\r
\r
-     protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,\r
-             String name,\r
-             float accuracy,\r
-             int maxNoOfResults,\r
-             List<Language> languages,\r
-             boolean highlightFragments) {\r
\r
-         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);\r
-         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.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.setCdmTypRestriction(clazz);\r
-         FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());\r
\r
-         fltq.addTerms(name, "nameCache", accuracy, 3);\r
\r
-          BooleanQuery finalQuery = new BooleanQuery(false);\r
\r
-          finalQuery.add(fltq, Occur.MUST);\r
\r
-         luceneSearch.setQuery(finalQuery);\r
\r
-         if(highlightFragments){\r
-             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-         }\r
-         return luceneSearch;\r
-     }\r
\r
-     protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,\r
-             String name,\r
-             boolean wildcard,\r
-             List<Language> languages,\r
-             boolean highlightFragments) {\r
-         BooleanQuery finalQuery = new BooleanQuery();\r
-         BooleanQuery textQuery = new BooleanQuery();\r
\r
-         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);\r
-         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.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.setCdmTypRestriction(clazz);\r
\r
-         if(name != null && !name.equals("")) {\r
-             if(wildcard) {\r
-                 textQuery.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);\r
-             } else {\r
-                 textQuery.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);\r
-             }\r
-         }\r
\r
-         luceneSearch.setQuery(textQuery);\r
\r
-         if(highlightFragments){\r
-             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());\r
-         }\r
-         return luceneSearch;\r
-     }\r
\r
-     @Override\r
-     public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(\r
-             String name,\r
-             float accuracy,\r
-             List<Language> languages,\r
-             boolean highlightFragments,\r
-             List<String> propertyPaths,\r
-             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {\r
\r
-         logger.info("Name to fuzzy search for : " + name);\r
-         // parse the input name\r
-         NonViralNameParserImpl parser = new NonViralNameParserImpl();\r
-         NonViralName nvn = parser.parseFullName(name);\r
-         if(name != null && !name.equals("") && nvn == null) {\r
-             throw new ParseException("Could not parse name " + name);\r
-         }\r
-         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);\r
\r
-         // --- execute search\r
-         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);\r
\r
\r
-         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
-         idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");\r
\r
-         // --- initialize taxa, highlight matches ....\r
-         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
\r
-         @SuppressWarnings("rawtypes")\r
-         List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(\r
-                 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
\r
-         return searchResults;\r
\r
-     }\r
\r
-     @Override\r
-     public List<DocumentSearchResult> findByNameFuzzySearch(\r
-             String name,\r
-             float accuracy,\r
-             List<Language> languages,\r
-             boolean highlightFragments,\r
-             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {\r
\r
-         logger.info("Name to fuzzy search for : " + name);\r
-         // parse the input name\r
-         NonViralNameParserImpl parser = new NonViralNameParserImpl();\r
-         NonViralName nvn = parser.parseFullName(name);\r
-         if(name != null && !name.equals("") && nvn == null) {\r
-             throw new ParseException("Could not parse name " + name);\r
-         }\r
-         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);\r
\r
-         // --- execute search\r
-         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);\r
\r
-         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
\r
-         // --- initialize taxa, highlight matches ....\r
-         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
\r
-         @SuppressWarnings("rawtypes")\r
-         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());\r
\r
-         return searchResults;\r
-     }\r
\r
-     @Override\r
-     public List<DocumentSearchResult> findByFuzzyNameCacheSearch(\r
-             String name,\r
-             float accuracy,\r
-             List<Language> languages,\r
-             boolean highlightFragments,\r
-             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {\r
\r
-         logger.info("Name to fuzzy search for : " + name);\r
\r
-         LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);\r
\r
-         // --- execute search\r
-         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);\r
-         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
\r
-         // --- initialize taxa, highlight matches ....\r
-         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
\r
-         @SuppressWarnings("rawtypes")\r
-         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());\r
\r
-         return searchResults;\r
-     }\r
\r
-     @Override\r
-     public List<DocumentSearchResult> findByNameExactSearch(\r
-             String name,\r
-             boolean wildcard,\r
-             List<Language> languages,\r
-             boolean highlightFragments,\r
-             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {\r
\r
-         logger.info("Name to exact search for : " + name);\r
\r
-         LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);\r
\r
-         // --- execute search\r
\r
\r
-         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);\r
\r
-         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
\r
-         // --- initialize taxa, highlight matches ....\r
-         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
\r
-         @SuppressWarnings("rawtypes")\r
-         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());\r
\r
-         return searchResults;\r
-     }\r
\r
-     @Override\r
-     public Pager<NameRelationship> pageNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,\r
-             Integer pageNumber, List<OrderHint> orderHints,   List<String> propertyPaths) {\r
-         List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-         return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-     }\r
\r
-     @Override\r
-     public Pager<NameRelationship> pageFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-         List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-         return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-     }\r
\r
-     @Override\r
-     public Pager<NameRelationship> pageToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-         List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,\r
-             Integer pageSize, Integer pageNumber) {\r
-         return getTypeDesignations(name, status, pageSize, pageNumber, null);\r
-     }\r
\r
-     @Override\r
-     public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,\r
-                 Integer pageSize, Integer pageNumber, List<String> propertyPaths){\r
-         Integer numberOfResults = dao.countTypeDesignations(name, status);\r
\r
-         List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();\r
-         if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {\r
-             results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);\r
-         }\r
\r
-         return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     /**\r
-      * FIXME Candidate for harmonization\r
-      * rename search\r
-      */\r
-     @Override\r
-     public Pager<TaxonNameBase> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,    Integer pageNumber, List<OrderHint> orderHints,\r
-             List<String> propertyPaths) {\r
-         Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);\r
\r
-         List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();\r
-         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)\r
-             results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);\r
-         }\r
\r
-         return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames() {\r
-         return dao.getUuidAndTitleCacheOfNames();\r
-     }\r
\r
-     @Override\r
-     public Pager<TaxonNameBase> findByName(Class<? extends TaxonNameBase> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {\r
-         Integer numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);\r
\r
-          List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();\r
-          if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)\r
-                 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);\r
-          }\r
\r
-           return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @Override\r
-     public HomotypicalGroup findHomotypicalGroup(UUID uuid) {\r
-         return homotypicalGroupDao.findByUuid(uuid);\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {\r
-         if (clazz == null){\r
-             clazz = TaxonNameBase.class;\r
-         }\r
-         super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);\r
-     }\r
\r
\r
-     @Override\r
-     protected void setOtherCachesNull(TaxonNameBase name) {\r
-         if (name.isInstanceOf(NonViralName.class)){\r
-             NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);\r
-             if (! nvn.isProtectedNameCache()){\r
-                 nvn.setNameCache(null, false);\r
-             }\r
-             if (! nvn.isProtectedAuthorshipCache()){\r
-                 nvn.setAuthorshipCache(null, false);\r
-             }\r
-             if (! nvn.isProtectedFullTitleCache()){\r
-                 nvn.setFullTitleCache(null, false);\r
-             }\r
-         }\r
-     }\r
\r
-     @Override\r
-     public List<TaggedText> getTaggedName(UUID uuid) {\r
-         TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);\r
-         List<TaggedText> taggedName = taxonNameBase.getTaggedName();\r
-         return taggedName;\r
-     }\r
\r
-     @Override\r
-     public DeleteResult isDeletable(TaxonNameBase name, DeleteConfiguratorBase config){\r
-       DeleteResult result = new DeleteResult();\r
-       //name = this.load(name.getUuid());\r
-       NameDeletionConfigurator nameConfig = null;\r
-       if (config instanceof NameDeletionConfigurator){\r
-               nameConfig = (NameDeletionConfigurator) config;\r
-       }else{\r
-                result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));\r
-                result.setError();\r
-                return result;\r
-       }\r
\r
-       if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){\r
-               HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);\r
\r
-               if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){\r
-                               result.addException(new Exception( "Name can't be deleted as it is a basionym."));\r
-                               result.setAbort();\r
-             }\r
-             if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){\r
-               result.addException(new Exception( "Name can't be deleted as it has a basionym."));\r
-               result.setAbort();\r
-             }\r
-             Set<NameRelationship> relationships = name.getNameRelations();\r
-             for (NameRelationship rel: relationships){\r
-               if (!rel.getType().equals(NameRelationshipType.BASIONYM())){\r
-                       result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));\r
-                       result.setAbort();\r
-                       break;\r
-               }\r
-             }\r
-         }\r
\r
-         //concepts\r
-         if (! name.getTaxonBases().isEmpty()){\r
-               result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));\r
-               result.setAbort();\r
-         }\r
\r
-         //hybrid relationships\r
-         if (name.isInstanceOf(NonViralName.class)){\r
-             NonViralName nvn = CdmBase.deproxy(name, NonViralName.class);\r
-             if (! nvn.getHybridParentRelations().isEmpty()){\r
-               result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));\r
-               result.setAbort();\r
-             }\r
-         }\r
-       Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);\r
-         for (CdmBase referencingObject : referencingObjects){\r
-             //DerivedUnit?.storedUnder\r
-             if (referencingObject.isInstanceOf(DerivedUnit.class)){\r
-                 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";\r
-                 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());\r
-                 result.addException(new ReferencedObjectUndeletableException(message));\r
-                 result.addRelatedObject(referencingObject);\r
-                 result.setAbort();\r
-             }\r
-             //DescriptionElementSource#nameUsedInSource\r
-             else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){\r
-                 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";\r
-                 result.addException(new ReferencedObjectUndeletableException(message));\r
-                 result.addRelatedObject(referencingObject);\r
-                 result.setAbort();\r
-             }\r
-             //NameTypeDesignation#typeName\r
-             else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){\r
-                 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";\r
-                 result.addException(new ReferencedObjectUndeletableException(message));\r
-                 result.addRelatedObject(referencingObject);\r
-                 result.setAbort();\r
-             }\r
-             //DeterminationEvent#taxonName\r
-             else if (referencingObject.isInstanceOf(DeterminationEvent.class)){\r
-                 String message = "Name can't be deleted as it is used as a determination event";\r
-                 result.addException(new ReferencedObjectUndeletableException(message));\r
-                 result.addRelatedObject(referencingObject);\r
-                 result.setAbort();\r
-         }\r
-         }\r
\r
-         //TODO inline references\r
\r
\r
-         if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){\r
-             String message = "Name can't be deleted as it is a replaced synonym.";\r
-             result.addException(new Exception(message));\r
-             result.setAbort();\r
-         }\r
-         if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){\r
-             String message = "Name can't be deleted as it has a replaced synonym.";\r
-             result.addException(new Exception(message));\r
-             result.setAbort();\r
-         }\r
-       return result;\r
\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = true)\r
-     public UpdateResult setAsGroupsBasionym(UUID nameUuid) {\r
-         TaxonNameBase name = dao.load(nameUuid);\r
-         UpdateResult result = new UpdateResult();\r
-         name.setAsGroupsBasionym();\r
-         result.addUpdatedObject(name);\r
-         return result;\r
\r
-     }\r
\r
-     @Override\r
-     public List<HashMap<String,String>> getNameRecords(){\r
\r
-               return dao.getNameRecords();\r
\r
-     }\r
\r
- }\r
+ // $Id$
+ /**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+ package eu.etaxonomy.cdm.api.service;
+ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+ import java.util.UUID;
+ import org.apache.log4j.Logger;
+ import org.apache.lucene.index.CorruptIndexException;
+ import org.apache.lucene.index.Term;
+ import org.apache.lucene.queryparser.classic.ParseException;
+ import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
+ import org.apache.lucene.search.BooleanClause.Occur;
+ import org.apache.lucene.search.BooleanQuery;
+ import org.apache.lucene.search.BooleanQuery.Builder;
+ import org.apache.lucene.search.TopDocs;
+ import org.apache.lucene.search.WildcardQuery;
+ import org.hibernate.criterion.Criterion;
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.beans.factory.annotation.Qualifier;
+ import org.springframework.stereotype.Service;
+ import org.springframework.transaction.annotation.Transactional;
+ import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
+ import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
+ import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
+ import eu.etaxonomy.cdm.api.service.pager.Pager;
+ import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
+ import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
+ import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
+ import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
+ import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
+ import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
+ import eu.etaxonomy.cdm.api.service.search.QueryFactory;
+ import eu.etaxonomy.cdm.api.service.search.SearchResult;
+ import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
+ import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+ import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+ import eu.etaxonomy.cdm.model.CdmBaseType;
+ import eu.etaxonomy.cdm.model.common.CdmBase;
+ import eu.etaxonomy.cdm.model.common.Language;
+ import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
+ import eu.etaxonomy.cdm.model.common.RelationshipBase;
+ import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
+ import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
+ import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
+ import eu.etaxonomy.cdm.model.name.HybridRelationship;
+ import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
+ import eu.etaxonomy.cdm.model.name.NameRelationship;
+ import eu.etaxonomy.cdm.model.name.NameRelationshipType;
+ import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
+ import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
+ import eu.etaxonomy.cdm.model.name.NonViralName;
+ import eu.etaxonomy.cdm.model.name.Rank;
+ import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
+ import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+ import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
+ import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+ import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+ import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
+ import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
+ import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
+ import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
+ import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
+ import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
+ import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
+ import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
+ import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
+ import eu.etaxonomy.cdm.persistence.query.MatchMode;
+ import eu.etaxonomy.cdm.persistence.query.OrderHint;
+ import eu.etaxonomy.cdm.strategy.cache.TaggedText;
+ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+ import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
+ @Service
+ @Transactional(readOnly = true)
+ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxonNameDao> implements INameService {
+     static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
+     @Autowired
+     protected ITermVocabularyDao vocabularyDao;
+     @Autowired
+     protected IOrderedTermVocabularyDao orderedVocabularyDao;
+     @Autowired
+     @Qualifier("refEntDao")
+     protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
+     @Autowired
+     private INomenclaturalStatusDao nomStatusDao;
+     @Autowired
+     private ITypeDesignationDao typeDesignationDao;
+     @Autowired
+     private IHomotypicalGroupDao homotypicalGroupDao;
+     @Autowired
+     private ICdmGenericDao genericDao;
+     @Autowired
+     private ILuceneIndexToolProvider luceneIndexToolProvider;
+     /**
+      * Constructor
+      */
+     public NameServiceImpl(){
+         if (logger.isDebugEnabled()) { logger.debug("Load NameService Bean"); }
+     }
+ //********************* METHODS ****************************************************************//
+     /* (non-Javadoc)
+      * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
+      */
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult delete(UUID nameUUID){
+         NameDeletionConfigurator config = new NameDeletionConfigurator();
+         DeleteResult result = delete(nameUUID, config);
+         return result;
+     }
+     @Override
+     public DeleteResult delete(TaxonNameBase name){
+         return delete(name.getUuid());
+     }
+     /* (non-Javadoc)
+      * @see eu.etaxonomy.cdm.api.service.INameService#delete(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.api.service.NameDeletionConfigurator)
+      */
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
+       DeleteResult result = new DeleteResult();
+               TaxonNameBase name = dao.load(nameUUID);
+       if (name == null){
+               result.setAbort();
+             return result;
+         }
+       try{
+               result = this.isDeletable(name, config);
+         }catch(Exception e){
+               result.addException(e);
+               result.setError();
+               return result;
+         }
+         if (result.isOk()){
+         //remove references to this name
+               removeNameRelationshipsByDeleteConfig(name, config);
+            //remove name from homotypical group
+             HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
+             if (homotypicalGroup != null){
+                 homotypicalGroup.removeTypifiedName(name, false);
+             }
+              //all type designation relationships are removed as they belong to the name
+               deleteTypeDesignation(name, null);
+       //              //type designations
+       //              if (! name.getTypeDesignations().isEmpty()){
+       //                      String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
+       //                      throw new ReferrencedObjectUndeletableException(message);
+       //              }
+               try{
+               UUID nameUuid = dao.delete(name);
+               }catch(Exception e){
+                       result.addException(e);
+                       result.setError();
+               }
+               return result;
+         }
+         return result;
+     }
+     /* (non-Javadoc)
+      * @see eu.etaxonomy.cdm.api.service.INameService#deleteTypeDesignation(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.model.name.TypeDesignationBase)
+      */
+     @Override
+     public void deleteTypeDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation){
+         if (name == null && typeDesignation == null){
+             return;
+         }else if (name != null && typeDesignation != null){
+             removeSingleDesignation(name, typeDesignation);
+         }else if (name != null){
+             Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());
+             for (Object o : designationSet){
+                 TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);
+                 removeSingleDesignation(name, desig);
+             }
+         }else if (typeDesignation != null){
+             Set<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());
+             for (Object o : nameSet){
+                 TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);
+                 removeSingleDesignation(singleName, typeDesignation);
+             }
+         }
+     }
+     /**
+      * @param name
+      * @param typeDesignation
+      */
+     private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {
+         name.removeTypeDesignation(typeDesignation);
+         if (typeDesignation.getTypifiedNames().isEmpty()){
+             typeDesignation.removeType();
+             typeDesignationDao.delete(typeDesignation);
+         }
+     }
+     /**
+      * @param name
+      * @param config
+      */
+     private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {
+         try {
+             if (config.isRemoveAllNameRelationships()){
+                 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
+                 for (NameRelationship rel : rels){
+                     name.removeNameRelationship(rel);
+                 }
+             }else{
+                 //relations to this name
+                 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
+                 for (NameRelationship rel : rels){
+                     if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
+                             name.removeNameRelationship(rel);
+                     }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
+                         name.removeNameRelationship(rel);
+                     }
+                 }
+                 //relations from this name
+                 rels = getModifiableSet(name.getRelationsFromThisName());
+                 for (NameRelationship rel : rels){
+                     if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType())  ){
+                         name.removeNameRelationship(rel);
+                     }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
+                         name.removeNameRelationship(rel);
+                     }
+                 }
+             }
+         } catch (Exception e) {
+             throw new RuntimeException(e);
+         }
+     }
+     /**
+      * @param name
+      * @return
+      */
+     private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
+         Set<NameRelationship> rels = new HashSet<NameRelationship>();
+         for (NameRelationship rel : relations){
+             rels.add(rel);
+         }
+         return rels;
+     }
+ //********************* METHODS ****************************************************************//
+     /**
+      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
+      * duplicate of findByName
+      */
+     @Override
+     @Deprecated
+     public List getNamesByName(String name){
+         return super.findCdmObjectsByTitle(name);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name findByName
+      */
+     @Override
+     public List<NonViralName> getNamesByNameCache(String nameCache){
+         List result = dao.findByName(nameCache, MatchMode.EXACT, null, null, null, null);
+         return result;
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name saveHomotypicalGroups
+      *
+      * findByTitle
+      */
+     @Override
+     public List<NonViralName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
+         List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);
+         return result;
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name saveHomotypicalGroups
+      *
+      * findByTitle
+      */
+     @Override
+     public List<NonViralName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
+         List result = dao.findByName(nameCache, matchMode, null, null, null ,propertyPaths);
+         return result;
+     }
+     /**
+      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
+      * Replace by load(UUID, propertyPaths)
+      */
+     @Override
+     @Deprecated
+     public NonViralName findNameByUuid(UUID uuid, List<String> propertyPaths){
+         return (NonViralName)dao.findByUuid(uuid, null ,propertyPaths);
+     }
+     /**
+      * TODO candidate for harmonization
+      */
+     @Override
+     public List getNamesByName(String name, CdmBase sessionObject){
+         return super.findCdmObjectsByTitle(name, sessionObject);
+     }
+     /**
+      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
+      * duplicate of findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths)
+      */
+     @Override
+     @Deprecated
+     public List findNamesByTitle(String title){
+         return super.findCdmObjectsByTitle(title);
+     }
+     /**
+      * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
+      * duplicate of findByTitle()
+      */
+     @Override
+     @Deprecated
+     public List findNamesByTitle(String title, CdmBase sessionObject){
+         return super.findCdmObjectsByTitle(title, sessionObject);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name saveHomotypicalGroups
+      */
+     @Override
+     @Transactional(readOnly = false)
+     public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
+         return homotypicalGroupDao.saveAll(homotypicalGroups);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name saveTypeDesignations
+      */
+     @Override
+     @Transactional(readOnly = false)
+     public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
+         return typeDesignationDao.saveAll(typeDesignationCollection);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name saveReferencedEntities
+      */
+     @Override
+     @Transactional(readOnly = false)
+     public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
+         return referencedEntityDao.saveAll(referencedEntityCollection);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name getNames
+      */
+     public List<TaxonNameBase> getAllNames(int limit, int start){
+         return dao.list(limit, start);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name getNomenclaturalStatus
+      */
+     @Override
+     public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
+         return nomStatusDao.list(limit, start);
+     }
+     /**
+      * TODO candidate for harmonization
+      * new name getTypeDesignations
+      */
+     @Override
+     public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
+         return typeDesignationDao.getAllTypeDesignations(limit, start);
+     }
+       /**
+      * FIXME Candidate for harmonization
+      * homotypicalGroupService.list
+      */
+     @Override
+     public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
+         return homotypicalGroupDao.list(limit, start);
+     }
+     /**
+      * FIXME Candidate for harmonization
+      * remove
+      */
+     @Override
+     @Deprecated
+     public List<RelationshipBase> getAllRelationships(int limit, int start){
+         return dao.getAllRelationships(limit, start);
+     }
+     @Override
+     @Autowired
+     protected void setDao(ITaxonNameDao dao) {
+         this.dao = dao;
+     }
+     @Override
+     public Pager<HybridRelationship> getHybridNames(NonViralName name,        HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         Integer numberOfResults = dao.countHybridNames(name, type);
+         List<HybridRelationship> results = new ArrayList<HybridRelationship>();
+         if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
+             results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
+         }
+         return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<NameRelationship> listNameRelationships(TaxonNameBase name,   Direction direction, NameRelationshipType type, Integer pageSize,
+             Integer pageNumber, List<OrderHint> orderHints,   List<String> propertyPaths) {
+         Integer numberOfResults = dao.countNameRelationships(name, direction, type);
+         List<NameRelationship> results = new ArrayList<NameRelationship>();
+         if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
+             results = dao.getNameRelationships(name, direction, type, pageSize,       pageNumber, orderHints, propertyPaths);
+         }
+         return results;
+     }
+     protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
+             NonViralName nvn,
+             float accuracy,
+             int maxNoOfResults,
+             List<Language> languages,
+             boolean highlightFragments) {
+         String similarity = Float.toString(accuracy);
+         String searchSuffix = "~" + similarity;
+         Builder finalQueryBuilder = new Builder();
+         finalQueryBuilder.setDisableCoord(false);
+         Builder textQueryBuilder = new Builder();
+         textQueryBuilder.setDisableCoord(false);
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
+         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+ //            SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
+ //            luceneSearch.setSortFields(sortFields);
+         // ---- search criteria
+         luceneSearch.setCdmTypRestriction(clazz);
+         FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
+         if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
+             fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
+         } else {
+             //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
+             textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
+         }
+         if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
+             fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
+         } else {
+             //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
+             textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
+         }
+         if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
+             fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
+         } else {
+             //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
+             textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
+         }
+         if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
+             fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
+         } else {
+             //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
+             textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
+         }
+         if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
+             fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
+         } else {
+             //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
+         }
+         textQueryBuilder.add(fltq, Occur.MUST);
+         BooleanQuery textQuery = textQueryBuilder.build();
+         finalQueryBuilder.add(textQuery, Occur.MUST);
+         luceneSearch.setQuery(finalQueryBuilder.build());
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
+             String name,
+             float accuracy,
+             int maxNoOfResults,
+             List<Language> languages,
+             boolean highlightFragments) {
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
+         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+ //            SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
+ //            luceneSearch.setSortFields(sortFields);
+         // ---- search criteria
+         luceneSearch.setCdmTypRestriction(clazz);
+         FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
+         fltq.addTerms(name, "nameCache", accuracy, 3);
+          BooleanQuery finalQuery = new BooleanQuery(false);
+          finalQuery.add(fltq, Occur.MUST);
+         luceneSearch.setQuery(finalQuery);
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
+             String name,
+             boolean wildcard,
+             List<Language> languages,
+             boolean highlightFragments) {
+         Builder textQueryBuilder = new Builder();
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
+         QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+ //            SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
+ //            luceneSearch.setSortFields(sortFields);
+         // ---- search criteria
+         luceneSearch.setCdmTypRestriction(clazz);
+         if(name != null && !name.equals("")) {
+             if(wildcard) {
+                 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
+             } else {
+                 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
+             }
+         }
+         luceneSearch.setQuery(textQueryBuilder.build());
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     @Override
+     public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(
+             String name,
+             float accuracy,
+             List<Language> languages,
+             boolean highlightFragments,
+             List<String> propertyPaths,
+             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+         logger.info("Name to fuzzy search for : " + name);
+         // parse the input name
+         NonViralNameParserImpl parser = new NonViralNameParserImpl();
+         NonViralName nvn = parser.parseFullName(name);
+         if(name != null && !name.equals("") && nvn == null) {
+             throw new ParseException("Could not parse name " + name);
+         }
+         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
+         // --- execute search
+         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         @SuppressWarnings("rawtypes")
+         List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         return searchResults;
+     }
+     @Override
+     public List<DocumentSearchResult> findByNameFuzzySearch(
+             String name,
+             float accuracy,
+             List<Language> languages,
+             boolean highlightFragments,
+             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+         logger.info("Name to fuzzy search for : " + name);
+         // parse the input name
+         NonViralNameParserImpl parser = new NonViralNameParserImpl();
+         NonViralName nvn = parser.parseFullName(name);
+         if(name != null && !name.equals("") && nvn == null) {
+             throw new ParseException("Could not parse name " + name);
+         }
+         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
+         // --- execute search
+         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         @SuppressWarnings("rawtypes")
+         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
+         return searchResults;
+     }
+     @Override
+     public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
+             String name,
+             float accuracy,
+             List<Language> languages,
+             boolean highlightFragments,
+             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+         logger.info("Name to fuzzy search for : " + name);
+         LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
+         // --- execute search
+         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         @SuppressWarnings("rawtypes")
+         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
+         return searchResults;
+     }
+     @Override
+     public List<DocumentSearchResult> findByNameExactSearch(
+             String name,
+             boolean wildcard,
+             List<Language> languages,
+             boolean highlightFragments,
+             int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+         logger.info("Name to exact search for : " + name);
+         LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
+         // --- execute search
+         TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         @SuppressWarnings("rawtypes")
+         List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
+         return searchResults;
+     }
+     @Override
+     public Pager<NameRelationship> pageNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
+             Integer pageNumber, List<OrderHint> orderHints,   List<String> propertyPaths) {
+         List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
+         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
+     }
+     @Override
+     public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
+     }
+     @Override
+     public Pager<NameRelationship> pageFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
+         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
+     }
+     @Override
+     public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
+     }
+     @Override
+     public Pager<NameRelationship> pageToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
+         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
+     }
+     @Override
+     public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
+             Integer pageSize, Integer pageNumber) {
+         return getTypeDesignations(name, status, pageSize, pageNumber, null);
+     }
+     @Override
+     public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
+                 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
+         Integer numberOfResults = dao.countTypeDesignations(name, status);
+         List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();
+         if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {
+             results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
+         }
+         return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
+     }
+     /**
+      * FIXME Candidate for harmonization
+      * rename search
+      */
+     @Override
+     public Pager<TaxonNameBase> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,    Integer pageNumber, List<OrderHint> orderHints,
+             List<String> propertyPaths) {
+         Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
+         List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
+         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
+             results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
+         }
+         return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames() {
+         return dao.getUuidAndTitleCacheOfNames();
+     }
+     @Override
+     public Pager<TaxonNameBase> findByName(Class<? extends TaxonNameBase> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
+         Integer numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
+          List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
+          if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
+                 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
+          }
+           return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
+         return homotypicalGroupDao.findByUuid(uuid);
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {
+         if (clazz == null){
+             clazz = TaxonNameBase.class;
+         }
+         super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+     }
+     @Override
+     protected void setOtherCachesNull(TaxonNameBase name) {
+         if (name.isInstanceOf(NonViralName.class)){
+             NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
+             if (! nvn.isProtectedNameCache()){
+                 nvn.setNameCache(null, false);
+             }
+             if (! nvn.isProtectedAuthorshipCache()){
+                 nvn.setAuthorshipCache(null, false);
+             }
+             if (! nvn.isProtectedFullTitleCache()){
+                 nvn.setFullTitleCache(null, false);
+             }
+         }
+     }
+     @Override
+     public List<TaggedText> getTaggedName(UUID uuid) {
+         TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);
+         List<TaggedText> taggedName = taxonNameBase.getTaggedName();
+         return taggedName;
+     }
+     @Override
+     public DeleteResult isDeletable(TaxonNameBase name, DeleteConfiguratorBase config){
+       DeleteResult result = new DeleteResult();
 -      name = this.load(name.getUuid());
++      //name = this.load(name.getUuid());
+       NameDeletionConfigurator nameConfig = null;
+       if (config instanceof NameDeletionConfigurator){
+               nameConfig = (NameDeletionConfigurator) config;
+       }else{
+                result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
+                result.setError();
+                return result;
+       }
+       if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
+               HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
+               if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
+                               result.addException(new Exception( "Name can't be deleted as it is a basionym."));
+                               result.setAbort();
+             }
+             if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
+               result.addException(new Exception( "Name can't be deleted as it has a basionym."));
+               result.setAbort();
+             }
+             Set<NameRelationship> relationships = name.getNameRelations();
+             for (NameRelationship rel: relationships){
+               if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
+                       result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
+                       result.setAbort();
+                       break;
+               }
+             }
+         }
+         //concepts
+         if (! name.getTaxonBases().isEmpty()){
+               result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
+               result.setAbort();
+         }
+         //hybrid relationships
+         if (name.isInstanceOf(NonViralName.class)){
+             NonViralName nvn = CdmBase.deproxy(name, NonViralName.class);
+             if (! nvn.getHybridParentRelations().isEmpty()){
+               result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));
+               result.setAbort();
+             }
+         }
+       Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
+         for (CdmBase referencingObject : referencingObjects){
+             //DerivedUnit?.storedUnder
+             if (referencingObject.isInstanceOf(DerivedUnit.class)){
+                 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
+                 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
+                 result.addException(new ReferencedObjectUndeletableException(message));
+                 result.addRelatedObject(referencingObject);
+                 result.setAbort();
+             }
+             //DescriptionElementSource#nameUsedInSource
+             else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
+                 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
+                 result.addException(new ReferencedObjectUndeletableException(message));
+                 result.addRelatedObject(referencingObject);
+                 result.setAbort();
+             }
+             //NameTypeDesignation#typeName
+             else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
+                 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
+                 result.addException(new ReferencedObjectUndeletableException(message));
+                 result.addRelatedObject(referencingObject);
+                 result.setAbort();
+             }
+             //DeterminationEvent#taxonName
+             else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
+                 String message = "Name can't be deleted as it is used as a determination event";
+                 result.addException(new ReferencedObjectUndeletableException(message));
+                 result.addRelatedObject(referencingObject);
+                 result.setAbort();
+         }
+         }
+         //TODO inline references
+         if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
+             String message = "Name can't be deleted as it is a replaced synonym.";
+             result.addException(new Exception(message));
+             result.setAbort();
+         }
+         if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
+             String message = "Name can't be deleted as it has a replaced synonym.";
+             result.addException(new Exception(message));
+             result.setAbort();
+         }
+       return result;
+     }
+     @Override
+     @Transactional(readOnly = true)
+     public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
+         TaxonNameBase name = dao.load(nameUuid);
+         UpdateResult result = new UpdateResult();
+         name.setAsGroupsBasionym();
+         result.addUpdatedObject(name);
+         return result;
+     }
+     @Override
+     public List<HashMap<String,String>> getNameRecords(){
+               return dao.getNameRecords();
+     }
+ }
index 03bbb4717cae2a5f7c5085c29d9aa69dffe74899,00f291e9791517139e445b3ad6fdf6e5bd401fcf..20015f2f9c116104911bea33917d4fb7bbdec4cc
- // $Id$
- /**
- * Copyright (C) 2007 EDIT
- * European Distributed Institute of Taxonomy
- * http://www.e-taxonomy.eu
- *
- * The contents of this file are subject to the Mozilla Public License Version 1.1
- * See LICENSE.TXT at the top of this package for the full license terms.
- */
- package eu.etaxonomy.cdm.api.service;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- import java.util.UUID;
- import org.apache.log4j.Logger;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
- import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
- import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
- import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;
- import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
- import eu.etaxonomy.cdm.api.service.pager.Pager;
- import eu.etaxonomy.cdm.api.service.pager.PagerUtils;
- import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
- import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
- import eu.etaxonomy.cdm.model.description.TaxonDescription;
- import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
- import eu.etaxonomy.cdm.model.name.TaxonNameBase;
- import eu.etaxonomy.cdm.model.reference.Reference;
- import eu.etaxonomy.cdm.model.taxon.Classification;
- import eu.etaxonomy.cdm.model.taxon.Synonym;
- import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
- import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
- import eu.etaxonomy.cdm.model.taxon.Taxon;
- import eu.etaxonomy.cdm.model.taxon.TaxonNaturalComparator;
- import eu.etaxonomy.cdm.model.taxon.TaxonNode;
- import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
- import eu.etaxonomy.cdm.model.taxon.TaxonNodeByNameComparator;
- import eu.etaxonomy.cdm.model.taxon.TaxonNodeByRankAndNameComparator;
- import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
- import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
- import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
- import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
- /**
-  * @author n.hoffmann
-  * @created Apr 9, 2010
-  * @version 1.0
-  */
- @Service
- @Transactional(readOnly = true)
- public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao> implements ITaxonNodeService{
-     private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);
-     @Autowired
-     private IBeanInitializer defaultBeanInitializer;
-     private final Comparator<? super TaxonNode> taxonNodeComparator = new TaxonNodeByNameComparator();
-     @Autowired
-     private ITaxonService taxonService;
-     @Autowired
-     private IClassificationService classService;
-     @Autowired
-     private IDefinedTermDao termDao;
-     @Override
-     public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,
-             List<String> propertyPaths, boolean recursive, NodeSortMode sortMode) {
-         taxonNode = dao.load(taxonNode.getUuid());
-         List<TaxonNode> childNodes;
-         if (recursive == true){
-               childNodes  = dao.listChildrenOf(taxonNode, null, null, null, recursive);
-         }else{
-               childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());
-         }
-         if (sortMode != null){
-             Comparator<TaxonNode> comparator = null;
-             if (sortMode.equals(NodeSortMode.NaturalOrder)){
-                 comparator = new TaxonNaturalComparator();
-             } else if (sortMode.equals(NodeSortMode.AlphabeticalOrder)){
-               comparator = new TaxonNodeByNameComparator();
-             } else if (sortMode.equals(NodeSortMode.RankAndAlphabeticalOrder)){
-               comparator = new TaxonNodeByRankAndNameComparator();
-             }
-               Collections.sort(childNodes, comparator);
-         }
-         defaultBeanInitializer.initializeAll(childNodes, propertyPaths);
-         return childNodes;
-     }
-     @Override
-     @Autowired
-     protected void setDao(ITaxonNodeDao dao) {
-         this.dao = dao;
-     }
-     @Override
-     @Transactional(readOnly = false)
-     public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, Reference citation, String citationMicroReference)  {
-         // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon
-         // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that
-         if (oldTaxonNode == null || newAcceptedTaxonNode == null || oldTaxonNode.getTaxon().getName() == null){
-             throw new IllegalArgumentException("A mandatory parameter was null.");
-         }
-         if(oldTaxonNode.equals(newAcceptedTaxonNode)){
-             throw new IllegalArgumentException("Taxon can not be made synonym of its own.");
-         }
-         Classification classification = oldTaxonNode.getClassification();
-         Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());
-         Taxon newAcceptedTaxon = (Taxon)this.taxonService.load(newAcceptedTaxonNode.getTaxon().getUuid());
-         // Move oldTaxon to newTaxon
-         //TaxonNameBase<?,?> synonymName = oldTaxon.getName();
-         TaxonNameBase<?,?> synonymName = (TaxonNameBase)HibernateProxyHelper.deproxy(oldTaxon.getName());
-         HomotypicalGroup group = synonymName.getHomotypicalGroup();
-         group = HibernateProxyHelper.deproxy(group, HomotypicalGroup.class);
-         if (synonymRelationshipType == null){
-             if (synonymName.isHomotypic(newAcceptedTaxon.getName())){
-                 synonymRelationshipType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
-             }else{
-                 synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
-             }
-         }
-         //set homotypic group
-         HomotypicalGroup newAcceptedTaxonHomotypicalgroup = newAcceptedTaxon.getHomotypicGroup();
-         newAcceptedTaxonHomotypicalgroup = HibernateProxyHelper.deproxy(newAcceptedTaxonHomotypicalgroup, HomotypicalGroup.class);
-         TaxonNameBase newAcceptedTaxonName = HibernateProxyHelper.deproxy(newAcceptedTaxon.getName(), TaxonNameBase.class);
-         // Move Synonym Relations to new Taxon
-         SynonymRelationship synonmyRelationship = newAcceptedTaxon.addSynonymName(synonymName,
-                 synonymRelationshipType, citation, citationMicroReference);
-          HomotypicalGroup homotypicalGroupAcceptedTaxon = synonmyRelationship.getSynonym().getHomotypicGroup();
-         // Move Synonym Relations to new Taxon
-         // From ticket 3163 we can move taxon with accepted name having homotypic synonyms
-         List<Synonym> synonymsInHomotypicalGroup = null;
-         //the synonyms of the homotypical group of the old taxon
-         if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
-               synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);
-         }
-         for(SynonymRelationship synRelation : oldTaxon.getSynonymRelations()){
-             SynonymRelationshipType srt;
-             if(synRelation.getSynonym().getName().getHomotypicalGroup()!= null
-                     && synRelation.getSynonym().getName().getHomotypicalGroup().equals(newAcceptedTaxonName.getHomotypicalGroup())) {
-                 srt = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
-             } else if(synRelation.getType() != null && synRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())) {
-               if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
-                       srt = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
-               } else{
-                       srt = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
-               }
-             } else {
-                 srt = synRelation.getType();
-             }
-             newAcceptedTaxon.addSynonym(synRelation.getSynonym(),
-                     srt,
-                     synRelation.getCitation(),
-                     synRelation.getCitationMicroReference());
-             /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){
-               homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());
-             }*/
-         }
-         // CHILD NODES
-         if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){
-               List<TaxonNode> childNodes = new ArrayList<TaxonNode>();
-               for (TaxonNode childNode : oldTaxonNode.getChildNodes()){
-                       childNodes.add(childNode);
-               }
-             for(TaxonNode childNode :childNodes){
-                 newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference()); // childNode.getSynonymToBeUsed()
-             }
-         }
-         //Move Taxon RelationShips to new Taxon
-         Set<TaxonRelationship> obsoleteTaxonRelationships = new HashSet<TaxonRelationship>();
-         for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){
-             Taxon fromTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());
-             Taxon toTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());
-             if (fromTaxon == oldTaxon){
-                 newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),
-                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
-             }else if(toTaxon == oldTaxon){
-                fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),
-                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());
-                taxonService.saveOrUpdate(fromTaxon);
-             }else{
-                 logger.warn("Taxon is not part of its own Taxonrelationship");
-             }
-             // Remove old relationships
-             fromTaxon.removeTaxonRelation(taxonRelationship);
-             toTaxon.removeTaxonRelation(taxonRelationship);
-             taxonRelationship.setToTaxon(null);
-             taxonRelationship.setFromTaxon(null);
-         }
-         //Move descriptions to new taxon
-         List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())
-         for(TaxonDescription description : descriptions){
-             String message = "Description copied from former accepted taxon: %s (Old title: %s)";
-             message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());
-             description.setTitleCache(message, true);
-             //oldTaxon.removeDescription(description, false);
-             newAcceptedTaxon.addDescription(description);
-         }
-         oldTaxon.clearDescriptions();
-         taxonService.update(newAcceptedTaxon);
-         taxonService.update(oldTaxon);
-         TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();
-         conf.setDeleteSynonymsIfPossible(false);
-         DeleteResult result = taxonService.isDeletable(oldTaxon, conf);
-         conf.setDeleteNameIfPossible(false);
-         if (result.isOk()){
-                result = taxonService.deleteTaxon(oldTaxon.getUuid(), conf, classification.getUuid());
-         }else{
-               result.setStatus(Status.OK);
-               TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();
-               config.setDeleteElement(false);
-               conf.setTaxonNodeConfig(config);
-               result.includeResult(deleteTaxonNode(oldTaxonNode, conf));
-         }
-         result.addUpdatedObject(newAcceptedTaxon);
-         result.addUpdatedObject(oldTaxon);
-         //oldTaxonNode.delete();
-         return result;
-     }
-     /* (non-Javadoc)
-      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#makeTaxonNodeASynonymOfAnotherTaxonNode(java.util.UUID, java.util.UUID, java.util.UUID, java.util.UUID, java.lang.String)
-      */
-     @Override
-     @Transactional(readOnly = false)
-     public UpdateResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,
-             UUID newAcceptedTaxonNodeUUID,
-             SynonymRelationshipType synonymRelationshipType,
-             Reference citation,
-             String citationMicroReference) {
-         TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);
-         TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();
-         TaxonNode newTaxonNode = dao.load(newAcceptedTaxonNodeUUID);
-         UpdateResult result = makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode,
-                 newTaxonNode,
-                 synonymRelationshipType,
-                 citation,
-                 citationMicroReference);
-         result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));
-         result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));
-         result.setCdmEntity(oldTaxonParentNode);
-         return result;
-     }
-     /* (non-Javadoc)
-      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)
-      */
-     @Override
-     @Transactional(readOnly = false)
-     public DeleteResult deleteTaxonNodes(List<TaxonNode> list, TaxonDeletionConfigurator config) {
-         if (config == null){
-               config = new TaxonDeletionConfigurator();
-         }
-         DeleteResult result = new DeleteResult();
-         List<UUID> deletedUUIDs = new ArrayList<UUID>();
-         Classification classification = null;
-         List<TaxonNode> taxonNodes = new ArrayList<TaxonNode>(list);
-         for (TaxonNode treeNode:taxonNodes){
-               if (treeNode != null){
-                       TaxonNode taxonNode;
-                   taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);
-                   TaxonNode parent = taxonNode.getParent();
-                       //check whether the node has children or the children are already deleted
-                   if(taxonNode.hasChildNodes()) {
-                       List<TaxonNode> children = new ArrayList<TaxonNode> ();
-                       List<TaxonNode> childNodesList = taxonNode.getChildNodes();
-                               children.addAll(childNodesList);
-                               int compare = config.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling.DELETE);
-                               boolean childHandling = (compare == 0)? true: false;
-                       if (childHandling){
-                               boolean changeDeleteTaxon = false;
-                               if (!config.getTaxonNodeConfig().isDeleteTaxon()){
-                                       config.getTaxonNodeConfig().setDeleteTaxon(true);
-                                       changeDeleteTaxon = true;
-                               }
-                               DeleteResult resultNodes = deleteTaxonNodes(children, config);
-                               if (!resultNodes.isOk()){
-                             result.addExceptions(resultNodes.getExceptions());
-                             result.setStatus(resultNodes.getStatus());
-                         }
-                               if (changeDeleteTaxon){
-                                       config.getTaxonNodeConfig().setDeleteTaxon(false);
-                               }
-                       } else {
-                               //move the children to the parent
-                               for (TaxonNode child: childNodesList){
-                                       parent.addChildNode(child, child.getReference(), child.getMicroReference());
-                               }
-                       }
-               }
-                   classification = taxonNode.getClassification();
-                   if (classification.getRootNode().equals(taxonNode)){
-                       classification.removeRootNode();
-                       classification = null;
-                   }else if (classification.getChildNodes().contains(taxonNode)){
-                       Taxon taxon = taxonNode.getTaxon();
-                       classification.deleteChildNode(taxonNode);
-                       //node is rootNode
-                       if (taxon != null){
-                               if (config.getTaxonNodeConfig().isDeleteTaxon()){
-                                   taxonService.saveOrUpdate(taxon);
-                                   saveOrUpdate(taxonNode);
-                                       TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
-                                       DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());
-                                       if (!resultTaxon.isOk()){
-                                 result.addExceptions(resultTaxon.getExceptions());
-                                 result.setStatus(resultTaxon.getStatus());
-                             }
-                               }
-                       }
-                       classification = null;
-                   } else {
-                       classification = null;
-                       Taxon taxon = taxonNode.getTaxon();
-                       taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);
-                       if (taxon != null){
-                               taxon.removeTaxonNode(taxonNode);
-                               if (config.getTaxonNodeConfig().isDeleteTaxon()){
-                                       TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();
-                                       saveOrUpdate(taxonNode);
-                                       taxonService.saveOrUpdate(taxon);
-                                       DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, null);
-                             if (!resultTaxon.isOk()){
-                                 result.addExceptions(resultTaxon.getExceptions());
-                                 result.setStatus(resultTaxon.getStatus());
-                             }
-                               }
-                       }
-                   }
-                   result.addUpdatedObject(parent);
-                   if(result.getCdmEntity() == null){
-                       result.setCdmEntity(taxonNode);
-                 }
-                   UUID uuid = dao.delete(taxonNode);
-                   logger.debug("Deleted node " +uuid.toString());
-               }
-         }
-         /*if (classification != null){
-             result.addUpdatedObject(classification);
-               DeleteResult resultClassification = classService.delete(classification);
-                if (!resultClassification.isOk()){
-                  result.addExceptions(resultClassification.getExceptions());
-                  result.setStatus(resultClassification.getStatus());
-              }
-         }*/
-         return result;
-     }
-     @Override
-     @Transactional(readOnly = false)
-     public DeleteResult deleteTaxonNodes(Collection<UUID> nodeUuids, TaxonDeletionConfigurator config) {
-         List<TaxonNode> nodes = new ArrayList<TaxonNode>();
-         for(UUID nodeUuid : nodeUuids) {
-             nodes.add(dao.load(nodeUuid));
-         }
-         return deleteTaxonNodes(nodes, config);
-     }
-     @Override
-     @Transactional(readOnly = false)
-     public DeleteResult deleteTaxonNode(UUID nodeUUID, TaxonDeletionConfigurator config) {
-       TaxonNode node = HibernateProxyHelper.deproxy(dao.load(nodeUUID), TaxonNode.class);
-       return deleteTaxonNode(node, config);
-     }
-     @Override
-     @Transactional(readOnly = false)
-     public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {
-         DeleteResult result = new DeleteResult();
-         if (node == null){
-             result.setAbort();
-             result.addException(new Exception("The TaxonNode was already deleted."));
-             return result;
-         }
-         Taxon taxon = null;
-         try{
-             taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());
-         }catch(NullPointerException e){
-             result.setAbort();
-             result.addException(new Exception("The Taxon was already deleted."));
-         }
-       TaxonNode parent = HibernateProxyHelper.deproxy(node.getParent(), TaxonNode.class);
-       if (config == null){
-               config = new TaxonDeletionConfigurator();
-       }
-       if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.MOVE_TO_PARENT)){
-          Object[] children = node.getChildNodes().toArray();
-          TaxonNode childNode;
-          for (Object child: children){
-              childNode = (TaxonNode) child;
-              parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());
-          }
-       }else{
-           deleteTaxonNodes(node.getChildNodes(), config);
-       }
-       if (taxon != null){
-               if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){
-                       result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());
-                       result.addUpdatedObject(parent);
-                       if (result.isOk()){
-                               return result;
-                       }
-               } else {
-                   result.addUpdatedObject(taxon);
-               }
-       }
-       result.setCdmEntity(node);
-       boolean success = taxon.removeTaxonNode(node);
-       dao.save(parent);
-       taxonService.saveOrUpdate(taxon);
-       result.addUpdatedObject(parent);
-       if (success){
-                       result.setStatus(Status.OK);
-                       parent = HibernateProxyHelper.deproxy(parent, TaxonNode.class);
-                       int index = parent.getChildNodes().indexOf(node);
-                       if (index > -1){
-                           parent.removeChild(index);
-                       }
-               if (!dao.delete(node, config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)).equals(null)){
-                       return result;
-               } else {
-                       result.setError();
-                       return result;
-               }
-       }else{
-           if (dao.findByUuid(node.getUuid()) != null){
-                       result.setError();
-                       result.addException(new Exception("The node can not be removed from the taxon."));
-               }
-               return result;
-       }
-     }
-     /* (non-Javadoc)
-      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)
-      */
-     @Override
-     public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {
-         return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);
-     }
-     @Override
-     public int countAllNodesForClassification(Classification classification) {
-         return dao.countTaxonOfAcceptedTaxaByClassification(classification);
-     }
-     @Override
-     @Transactional
-     public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, boolean isParent){
-         TaxonNode taxonNode = dao.load(taxonNodeUuid);
-       TaxonNode targetNode = dao.load(targetNodeUuid);
-       return moveTaxonNode(taxonNode, targetNode, isParent);
-     }
-     @Override
-     @Transactional
-     public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent, boolean isParent){
-         UpdateResult result = new UpdateResult();
-         Integer sortIndex;
-         if (isParent){
-             sortIndex = newParent.getChildNodes().size();
-         }else{
-             sortIndex = newParent.getSortIndex() +1;
-             newParent = newParent.getParent();
-         }
-         result.addUpdatedObject(newParent);
-         result.addUpdatedObject(taxonNode.getParent());
-         result.setCdmEntity(taxonNode);
-         newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(),  taxonNode.getMicroReference());
-         dao.saveOrUpdate(newParent);
-         return result;
-     }
-     @Override
-     @Transactional
-     public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, boolean isParent){
-         UpdateResult result = new UpdateResult();
-         TaxonNode targetNode = dao.load(newParentNodeUuid);
-         for (UUID taxonNodeUuid: taxonNodeUuids){
-             TaxonNode taxonNode = dao.load(taxonNodeUuid);
-             result.includeResult(moveTaxonNode(taxonNode,targetNode, isParent));
-         }
-         return result;
-     }
-     @Override
-     public Pager<TaxonNodeAgentRelation> pageTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
-             UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
-         List<TaxonNodeAgentRelation> records = null;
-         long count = dao.countTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid);
-         if(PagerUtils.hasResultsInRange(count, pageIndex, pageSize)) {
-             records = dao.listTaxonNodeAgentRelations(taxonUuid, classificationUuid,
-                     agentUuid, rankUuid, relTypeUuid, PagerUtils.startFor(pageSize, pageIndex), PagerUtils.limitFor(pageSize), propertyPaths);
-         }
-         Pager<TaxonNodeAgentRelation> pager = new DefaultPagerImpl<TaxonNodeAgentRelation>(pageIndex, count, pageSize, records);
-         return pager;
-     }
- }
+ // $Id$\r
+ /**\r
+ * Copyright (C) 2007 EDIT\r
+ * European Distributed Institute of Taxonomy\r
+ * http://www.e-taxonomy.eu\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License Version 1.1\r
+ * See LICENSE.TXT at the top of this package for the full license terms.\r
+ */\r
\r
+ package eu.etaxonomy.cdm.api.service;\r
\r
+ import java.util.ArrayList;\r
+ import java.util.Collection;\r
+ import java.util.Collections;\r
+ import java.util.Comparator;\r
+ import java.util.HashSet;\r
+ import java.util.List;\r
+ import java.util.Set;\r
+ import java.util.UUID;\r
\r
+ import org.apache.log4j.Logger;\r
+ import org.springframework.beans.factory.annotation.Autowired;\r
+ import org.springframework.stereotype.Service;\r
+ import org.springframework.transaction.annotation.Transactional;\r
\r
+ import eu.etaxonomy.cdm.api.service.UpdateResult.Status;\r
+ import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;\r
+ import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;\r
+ import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator;\r
+ import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;\r
+ import eu.etaxonomy.cdm.api.service.pager.Pager;\r
+ import eu.etaxonomy.cdm.api.service.pager.PagerUtils;\r
+ import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+ import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
+ import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+ import eu.etaxonomy.cdm.model.name.HomotypicalGroup;\r
+ import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
+ import eu.etaxonomy.cdm.model.reference.Reference;\r
+ import eu.etaxonomy.cdm.model.taxon.Classification;\r
+ import eu.etaxonomy.cdm.model.taxon.Synonym;\r
+ import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;\r
+ import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;\r
+ import eu.etaxonomy.cdm.model.taxon.Taxon;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonNaturalComparator;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonNodeByNameComparator;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonNodeByRankAndNameComparator;\r
+ import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
+ import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;\r
+ import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;\r
+ import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;\r
\r
+ /**\r
+  * @author n.hoffmann\r
+  * @created Apr 9, 2010\r
+  * @version 1.0\r
+  */\r
+ @Service\r
+ @Transactional(readOnly = true)\r
+ public class TaxonNodeServiceImpl extends AnnotatableServiceBase<TaxonNode, ITaxonNodeDao> implements ITaxonNodeService{\r
+     private static final Logger logger = Logger.getLogger(TaxonNodeServiceImpl.class);\r
\r
+     @Autowired\r
+     private IBeanInitializer defaultBeanInitializer;\r
\r
+     private final Comparator<? super TaxonNode> taxonNodeComparator = new TaxonNodeByNameComparator();\r
\r
+     @Autowired\r
+     private ITaxonService taxonService;\r
\r
+     @Autowired\r
+     private IClassificationService classService;\r
\r
+     @Autowired\r
+     private IDefinedTermDao termDao;\r
\r
+     @Override\r
+     public List<TaxonNode> loadChildNodesOfTaxonNode(TaxonNode taxonNode,\r
+             List<String> propertyPaths, boolean recursive, NodeSortMode sortMode) {\r
+         taxonNode = dao.load(taxonNode.getUuid());\r
+         List<TaxonNode> childNodes;\r
+         if (recursive == true){\r
+               childNodes  = dao.listChildrenOf(taxonNode, null, null, null, recursive);\r
+         }else{\r
+               childNodes = new ArrayList<TaxonNode>(taxonNode.getChildNodes());\r
+         }\r
+         if (sortMode != null){\r
+             Comparator<TaxonNode> comparator = null;\r
+             if (sortMode.equals(NodeSortMode.NaturalOrder)){\r
+                 comparator = new TaxonNaturalComparator();\r
+             } else if (sortMode.equals(NodeSortMode.AlphabeticalOrder)){\r
+               comparator = new TaxonNodeByNameComparator();\r
+             } else if (sortMode.equals(NodeSortMode.RankAndAlphabeticalOrder)){\r
+               comparator = new TaxonNodeByRankAndNameComparator();\r
+             }\r
+               Collections.sort(childNodes, comparator);\r
+         }\r
+         defaultBeanInitializer.initializeAll(childNodes, propertyPaths);\r
+         return childNodes;\r
+     }\r
\r
+     @Override\r
+     @Autowired\r
+     protected void setDao(ITaxonNodeDao dao) {\r
+         this.dao = dao;\r
+     }\r
\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public DeleteResult makeTaxonNodeASynonymOfAnotherTaxonNode(TaxonNode oldTaxonNode, TaxonNode newAcceptedTaxonNode, SynonymRelationshipType synonymRelationshipType, Reference citation, String citationMicroReference)  {\r
\r
\r
+         // TODO at the moment this method only moves synonym-, concept relations and descriptions to the new accepted taxon\r
+         // in a future version we also want to move cdm data like annotations, marker, so., but we will need a policy for that\r
+         if (oldTaxonNode == null || newAcceptedTaxonNode == null || oldTaxonNode.getTaxon().getName() == null){\r
+             throw new IllegalArgumentException("A mandatory parameter was null.");\r
+         }\r
\r
+         if(oldTaxonNode.equals(newAcceptedTaxonNode)){\r
+             throw new IllegalArgumentException("Taxon can not be made synonym of its own.");\r
+         }\r
\r
\r
+         Classification classification = oldTaxonNode.getClassification();\r
+         Taxon oldTaxon = (Taxon) HibernateProxyHelper.deproxy(oldTaxonNode.getTaxon());\r
+         Taxon newAcceptedTaxon = (Taxon)this.taxonService.load(newAcceptedTaxonNode.getTaxon().getUuid());\r
+         // Move oldTaxon to newTaxon\r
+         //TaxonNameBase<?,?> synonymName = oldTaxon.getName();\r
+         TaxonNameBase<?,?> synonymName = (TaxonNameBase)HibernateProxyHelper.deproxy(oldTaxon.getName());\r
+         HomotypicalGroup group = synonymName.getHomotypicalGroup();\r
+         group = HibernateProxyHelper.deproxy(group, HomotypicalGroup.class);\r
+         if (synonymRelationshipType == null){\r
+             if (synonymName.isHomotypic(newAcceptedTaxon.getName())){\r
+                 synonymRelationshipType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+             }else{\r
+                 synonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+             }\r
+         }\r
\r
+         //set homotypic group\r
+         HomotypicalGroup newAcceptedTaxonHomotypicalgroup = newAcceptedTaxon.getHomotypicGroup();\r
+         newAcceptedTaxonHomotypicalgroup = HibernateProxyHelper.deproxy(newAcceptedTaxonHomotypicalgroup, HomotypicalGroup.class);\r
+         TaxonNameBase newAcceptedTaxonName = HibernateProxyHelper.deproxy(newAcceptedTaxon.getName(), TaxonNameBase.class);\r
+         // Move Synonym Relations to new Taxon\r
+         SynonymRelationship synonmyRelationship = newAcceptedTaxon.addSynonymName(synonymName,\r
+                 synonymRelationshipType, citation, citationMicroReference);\r
+          HomotypicalGroup homotypicalGroupAcceptedTaxon = synonmyRelationship.getSynonym().getHomotypicGroup();\r
+         // Move Synonym Relations to new Taxon\r
+         // From ticket 3163 we can move taxon with accepted name having homotypic synonyms\r
+         List<Synonym> synonymsInHomotypicalGroup = null;\r
\r
+         //the synonyms of the homotypical group of the old taxon\r
+         if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){\r
+               synonymsInHomotypicalGroup = oldTaxon.getSynonymsInGroup(group);\r
+         }\r
\r
+         for(SynonymRelationship synRelation : oldTaxon.getSynonymRelations()){\r
+             SynonymRelationshipType srt;\r
+             if(synRelation.getSynonym().getName().getHomotypicalGroup()!= null\r
+                     && synRelation.getSynonym().getName().getHomotypicalGroup().equals(newAcceptedTaxonName.getHomotypicalGroup())) {\r
+                 srt = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+             } else if(synRelation.getType() != null && synRelation.getType().equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())) {\r
+               if (synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){\r
+                       srt = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();\r
+               } else{\r
+                       srt = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
+               }\r
+             } else {\r
+                 srt = synRelation.getType();\r
\r
+             }\r
\r
+             newAcceptedTaxon.addSynonym(synRelation.getSynonym(),\r
+                     srt,\r
+                     synRelation.getCitation(),\r
+                     synRelation.getCitationMicroReference());\r
\r
+             /*if (synonymsInHomotypicalGroup.contains(synRelation.getSynonym()) && srt.equals(SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF())){\r
+               homotypicalGroupAcceptedTaxon.addTypifiedName(synRelation.getSynonym().getName());\r
+             }*/\r
\r
+         }\r
\r
\r
\r
\r
\r
+         // CHILD NODES\r
+         if(oldTaxonNode.getChildNodes() != null && oldTaxonNode.getChildNodes().size() != 0){\r
+               List<TaxonNode> childNodes = new ArrayList<TaxonNode>();\r
+               for (TaxonNode childNode : oldTaxonNode.getChildNodes()){\r
+                       childNodes.add(childNode);\r
+               }\r
+             for(TaxonNode childNode :childNodes){\r
+                 newAcceptedTaxonNode.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference()); // childNode.getSynonymToBeUsed()\r
+             }\r
+         }\r
\r
+         //Move Taxon RelationShips to new Taxon\r
+         Set<TaxonRelationship> obsoleteTaxonRelationships = new HashSet<TaxonRelationship>();\r
+         for(TaxonRelationship taxonRelationship : oldTaxon.getTaxonRelations()){\r
+             Taxon fromTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getFromTaxon());\r
+             Taxon toTaxon = (Taxon) HibernateProxyHelper.deproxy(taxonRelationship.getToTaxon());\r
+             if (fromTaxon == oldTaxon){\r
+                 newAcceptedTaxon.addTaxonRelation(taxonRelationship.getToTaxon(), taxonRelationship.getType(),\r
+                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());\r
\r
+             }else if(toTaxon == oldTaxon){\r
+                fromTaxon.addTaxonRelation(newAcceptedTaxon, taxonRelationship.getType(),\r
+                         taxonRelationship.getCitation(), taxonRelationship.getCitationMicroReference());\r
+                taxonService.saveOrUpdate(fromTaxon);\r
\r
+             }else{\r
+                 logger.warn("Taxon is not part of its own Taxonrelationship");\r
+             }\r
+             // Remove old relationships\r
\r
+             fromTaxon.removeTaxonRelation(taxonRelationship);\r
+             toTaxon.removeTaxonRelation(taxonRelationship);\r
+             taxonRelationship.setToTaxon(null);\r
+             taxonRelationship.setFromTaxon(null);\r
+         }\r
\r
\r
+         //Move descriptions to new taxon\r
+         List<TaxonDescription> descriptions = new ArrayList<TaxonDescription>( oldTaxon.getDescriptions()); //to avoid concurrent modification errors (newAcceptedTaxon.addDescription() modifies also oldtaxon.descritpions())\r
+         for(TaxonDescription description : descriptions){\r
+             String message = "Description copied from former accepted taxon: %s (Old title: %s)";\r
+             message = String.format(message, oldTaxon.getTitleCache(), description.getTitleCache());\r
+             description.setTitleCache(message, true);\r
+             //oldTaxon.removeDescription(description, false);\r
+             newAcceptedTaxon.addDescription(description);\r
+         }\r
+         oldTaxon.clearDescriptions();\r
\r
+         taxonService.update(newAcceptedTaxon);\r
\r
+         taxonService.update(oldTaxon);\r
\r
+         TaxonDeletionConfigurator conf = new TaxonDeletionConfigurator();\r
+         conf.setDeleteSynonymsIfPossible(false);\r
+         DeleteResult result = taxonService.isDeletable(oldTaxon, conf);\r
+         conf.setDeleteNameIfPossible(false);\r
\r
+         if (result.isOk()){\r
+                result = taxonService.deleteTaxon(oldTaxon.getUuid(), conf, classification.getUuid());\r
+         }else{\r
+               result.setStatus(Status.OK);\r
+               TaxonNodeDeletionConfigurator config = new TaxonNodeDeletionConfigurator();\r
+               config.setDeleteElement(false);\r
+               conf.setTaxonNodeConfig(config);\r
+               result.includeResult(deleteTaxonNode(oldTaxonNode, conf));\r
+         }\r
+         result.addUpdatedObject(newAcceptedTaxon);\r
+         result.addUpdatedObject(oldTaxon);\r
\r
+         //oldTaxonNode.delete();\r
+         return result;\r
+     }\r
\r
\r
\r
+     /* (non-Javadoc)\r
+      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#makeTaxonNodeASynonymOfAnotherTaxonNode(java.util.UUID, java.util.UUID, java.util.UUID, java.util.UUID, java.lang.String)\r
+      */\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public UpdateResult makeTaxonNodeASynonymOfAnotherTaxonNode(UUID oldTaxonNodeUuid,\r
+             UUID newAcceptedTaxonNodeUUID,\r
+             SynonymRelationshipType synonymRelationshipType,\r
+             Reference citation,\r
+             String citationMicroReference) {\r
\r
+         TaxonNode oldTaxonNode = dao.load(oldTaxonNodeUuid);\r
+         TaxonNode oldTaxonParentNode = oldTaxonNode.getParent();\r
+         TaxonNode newTaxonNode = dao.load(newAcceptedTaxonNodeUUID);\r
\r
+         UpdateResult result = makeTaxonNodeASynonymOfAnotherTaxonNode(oldTaxonNode,\r
+                 newTaxonNode,\r
+                 synonymRelationshipType,\r
+                 citation,\r
+                 citationMicroReference);\r
+         result.addUpdatedCdmId(new CdmEntityIdentifier(oldTaxonParentNode.getId(), TaxonNode.class));\r
+         result.addUpdatedCdmId(new CdmEntityIdentifier(newTaxonNode.getId(), TaxonNode.class));\r
+         result.setCdmEntity(oldTaxonParentNode);\r
+         return result;\r
+     }\r
\r
+     /* (non-Javadoc)\r
+      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#deleteTaxonNodes(java.util.List)\r
+      */\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public DeleteResult deleteTaxonNodes(List<TaxonNode> list, TaxonDeletionConfigurator config) {\r
\r
+         if (config == null){\r
+               config = new TaxonDeletionConfigurator();\r
+         }\r
+         DeleteResult result = new DeleteResult();\r
+         List<UUID> deletedUUIDs = new ArrayList<UUID>();\r
+         Classification classification = null;\r
+         List<TaxonNode> taxonNodes = new ArrayList<TaxonNode>(list);\r
+         for (TaxonNode treeNode:taxonNodes){\r
+               if (treeNode != null){\r
\r
+                       TaxonNode taxonNode;\r
+                   taxonNode = HibernateProxyHelper.deproxy(treeNode, TaxonNode.class);\r
+                   TaxonNode parent = taxonNode.getParent();\r
+                       //check whether the node has children or the children are already deleted\r
+                   if(taxonNode.hasChildNodes()) {\r
+                       List<TaxonNode> children = new ArrayList<TaxonNode> ();\r
+                       List<TaxonNode> childNodesList = taxonNode.getChildNodes();\r
+                               children.addAll(childNodesList);\r
+                               int compare = config.getTaxonNodeConfig().getChildHandling().compareTo(ChildHandling.DELETE);\r
+                               boolean childHandling = (compare == 0)? true: false;\r
+                       if (childHandling){\r
+                               boolean changeDeleteTaxon = false;\r
+                               if (!config.getTaxonNodeConfig().isDeleteTaxon()){\r
+                                       config.getTaxonNodeConfig().setDeleteTaxon(true);\r
+                                       changeDeleteTaxon = true;\r
+                               }\r
+                               DeleteResult resultNodes = deleteTaxonNodes(children, config);\r
+                               if (!resultNodes.isOk()){\r
+                             result.addExceptions(resultNodes.getExceptions());\r
+                             result.setStatus(resultNodes.getStatus());\r
+                         }\r
+                               if (changeDeleteTaxon){\r
+                                       config.getTaxonNodeConfig().setDeleteTaxon(false);\r
+                               }\r
\r
+                       } else {\r
+                               //move the children to the parent\r
\r
+                               for (TaxonNode child: childNodesList){\r
+                                       parent.addChildNode(child, child.getReference(), child.getMicroReference());\r
+                               }\r
\r
+                       }\r
+               }\r
\r
+                   classification = taxonNode.getClassification();\r
\r
+                   if (classification.getRootNode().equals(taxonNode)){\r
+                       classification.removeRootNode();\r
+                       classification = null;\r
+                   }else if (classification.getChildNodes().contains(taxonNode)){\r
+                       Taxon taxon = taxonNode.getTaxon();\r
+                       classification.deleteChildNode(taxonNode);\r
\r
+                       //node is rootNode\r
+                       if (taxon != null){\r
\r
+                               if (config.getTaxonNodeConfig().isDeleteTaxon()){\r
+                                   taxonService.saveOrUpdate(taxon);\r
+                                   saveOrUpdate(taxonNode);\r
\r
+                                       TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();\r
+                                       DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, classification.getUuid());\r
+                                       if (!resultTaxon.isOk()){\r
+                                 result.addExceptions(resultTaxon.getExceptions());\r
+                                 result.setStatus(resultTaxon.getStatus());\r
+                             }\r
\r
+                               }\r
+                       }\r
+                       classification = null;\r
\r
+                   } else {\r
+                       classification = null;\r
+                       Taxon taxon = taxonNode.getTaxon();\r
+                       taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);\r
+                       if (taxon != null){\r
+                               taxon.removeTaxonNode(taxonNode);\r
+                               if (config.getTaxonNodeConfig().isDeleteTaxon()){\r
+                                       TaxonDeletionConfigurator configNew = new TaxonDeletionConfigurator();\r
+                                       saveOrUpdate(taxonNode);\r
+                                       taxonService.saveOrUpdate(taxon);\r
+                                       DeleteResult resultTaxon = taxonService.deleteTaxon(taxon.getUuid(), configNew, null);\r
\r
+                             if (!resultTaxon.isOk()){\r
+                                 result.addExceptions(resultTaxon.getExceptions());\r
+                                 result.setStatus(resultTaxon.getStatus());\r
+                             }\r
+                               }\r
+                       }\r
\r
+                   }\r
\r
+                   result.addUpdatedObject(parent);\r
+                   if(result.getCdmEntity() == null){\r
+                       result.setCdmEntity(taxonNode);\r
+                 }\r
+                   UUID uuid = dao.delete(taxonNode);\r
+                   logger.debug("Deleted node " +uuid.toString());\r
\r
+               }\r
+         }\r
+         /*if (classification != null){\r
+             result.addUpdatedObject(classification);\r
+               DeleteResult resultClassification = classService.delete(classification);\r
+                if (!resultClassification.isOk()){\r
+                  result.addExceptions(resultClassification.getExceptions());\r
+                  result.setStatus(resultClassification.getStatus());\r
+              }\r
+         }*/\r
+         return result;\r
\r
+     }\r
\r
\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public DeleteResult deleteTaxonNodes(Collection<UUID> nodeUuids, TaxonDeletionConfigurator config) {\r
+         List<TaxonNode> nodes = new ArrayList<TaxonNode>();\r
+         for(UUID nodeUuid : nodeUuids) {\r
+             nodes.add(dao.load(nodeUuid));\r
+         }\r
+         return deleteTaxonNodes(nodes, config);\r
+     }\r
\r
\r
\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public DeleteResult deleteTaxonNode(UUID nodeUUID, TaxonDeletionConfigurator config) {\r
++\r
+       TaxonNode node = HibernateProxyHelper.deproxy(dao.load(nodeUUID), TaxonNode.class);\r
+       return deleteTaxonNode(node, config);\r
+     }\r
\r
+     @Override\r
+     @Transactional(readOnly = false)\r
+     public DeleteResult deleteTaxonNode(TaxonNode node, TaxonDeletionConfigurator config) {\r
++        DeleteResult result = new DeleteResult();\r
++        if (node == null){\r
++            result.setAbort();\r
++            result.addException(new Exception("The TaxonNode was already deleted."));\r
++            return result;\r
++        }\r
++        Taxon taxon = null;\r
++        try{\r
++            taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());\r
++        }catch(NullPointerException e){\r
++            result.setAbort();\r
++            result.addException(new Exception("The Taxon was already deleted."));\r
\r
 -      Taxon taxon = (Taxon)HibernateProxyHelper.deproxy(node.getTaxon());\r
++        }\r
+       TaxonNode parent = HibernateProxyHelper.deproxy(node.getParent(), TaxonNode.class);\r
+       if (config == null){\r
+               config = new TaxonDeletionConfigurator();\r
+       }\r
 -      DeleteResult result = new DeleteResult();\r
++\r
++\r
\r
+       if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.MOVE_TO_PARENT)){\r
+          Object[] children = node.getChildNodes().toArray();\r
+          TaxonNode childNode;\r
+          for (Object child: children){\r
+              childNode = (TaxonNode) child;\r
+              parent.addChildNode(childNode, childNode.getReference(), childNode.getMicroReference());\r
+          }\r
+       }else{\r
+           deleteTaxonNodes(node.getChildNodes(), config);\r
+       }\r
\r
 -      if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){\r
 -              result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());\r
 -              result.addUpdatedObject(parent);\r
 -              if (result.isOk()){\r
 -                      return result;\r
 -              }\r
 -      } else {\r
 -          result.addUpdatedObject(taxon);\r
++      if (taxon != null){\r
++              if (config.getTaxonNodeConfig().isDeleteTaxon() && (config.isDeleteInAllClassifications() || taxon.getTaxonNodes().size() == 1)){\r
++                      result = taxonService.deleteTaxon(taxon.getUuid(), config, node.getClassification().getUuid());\r
++                      result.addUpdatedObject(parent);\r
++                      if (result.isOk()){\r
++                              return result;\r
++                      }\r
++              } else {\r
++                  result.addUpdatedObject(taxon);\r
++              }\r
+       }\r
 -\r
+       result.setCdmEntity(node);\r
+       boolean success = taxon.removeTaxonNode(node);\r
+       dao.save(parent);\r
+       taxonService.saveOrUpdate(taxon);\r
+       result.addUpdatedObject(parent);\r
\r
+       if (success){\r
+                       result.setStatus(Status.OK);\r
+                       parent = HibernateProxyHelper.deproxy(parent, TaxonNode.class);\r
+                       int index = parent.getChildNodes().indexOf(node);\r
+                       if (index > -1){\r
+                           parent.removeChild(index);\r
+                       }\r
+               if (!dao.delete(node, config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)).equals(null)){\r
+                       return result;\r
+               } else {\r
+                       result.setError();\r
+                       return result;\r
+               }\r
+       }else{\r
+           if (dao.findByUuid(node.getUuid()) != null){\r
+                       result.setError();\r
+                       result.addException(new Exception("The node can not be removed from the taxon."));\r
+               }\r
+               return result;\r
+       }\r
\r
\r
\r
+     }\r
\r
\r
+     /* (non-Javadoc)\r
+      * @see eu.etaxonomy.cdm.api.service.ITaxonNodeService#listAllNodesForClassification(eu.etaxonomy.cdm.model.taxon.Classification, int, int)\r
+      */\r
+     @Override\r
+     public List<TaxonNode> listAllNodesForClassification(Classification classification, Integer start, Integer end) {\r
+         return dao.getTaxonOfAcceptedTaxaByClassification(classification, start, end);\r
+     }\r
\r
+     @Override\r
+     public int countAllNodesForClassification(Classification classification) {\r
+         return dao.countTaxonOfAcceptedTaxaByClassification(classification);\r
+     }\r
\r
+     @Override\r
+     @Transactional\r
+     public UpdateResult moveTaxonNode(UUID taxonNodeUuid, UUID targetNodeUuid, boolean isParent){\r
+         TaxonNode taxonNode = dao.load(taxonNodeUuid);\r
+       TaxonNode targetNode = dao.load(targetNodeUuid);\r
+       return moveTaxonNode(taxonNode, targetNode, isParent);\r
+     }\r
\r
+     @Override\r
+     @Transactional\r
+     public UpdateResult moveTaxonNode(TaxonNode taxonNode, TaxonNode newParent, boolean isParent){\r
+         UpdateResult result = new UpdateResult();\r
\r
+         Integer sortIndex;\r
+         if (isParent){\r
\r
+             sortIndex = newParent.getChildNodes().size();\r
+         }else{\r
+             sortIndex = newParent.getSortIndex() +1;\r
+             newParent = newParent.getParent();\r
+         }\r
+         result.addUpdatedObject(newParent);\r
+         result.addUpdatedObject(taxonNode.getParent());\r
+         result.setCdmEntity(taxonNode);\r
+         newParent.addChildNode(taxonNode, sortIndex, taxonNode.getReference(),  taxonNode.getMicroReference());\r
+         dao.saveOrUpdate(newParent);\r
\r
+         return result;\r
+     }\r
\r
\r
\r
+     @Override\r
+     @Transactional\r
+     public UpdateResult moveTaxonNodes(Set<UUID> taxonNodeUuids, UUID newParentNodeUuid, boolean isParent){\r
+         UpdateResult result = new UpdateResult();\r
+         TaxonNode targetNode = dao.load(newParentNodeUuid);\r
+         for (UUID taxonNodeUuid: taxonNodeUuids){\r
+             TaxonNode taxonNode = dao.load(taxonNodeUuid);\r
+             result.includeResult(moveTaxonNode(taxonNode,targetNode, isParent));\r
+         }\r
+         return result;\r
+     }\r
\r
+     @Override\r
+     public Pager<TaxonNodeAgentRelation> pageTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,\r
+             UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer pageSize, Integer pageIndex, List<String> propertyPaths) {\r
\r
\r
+         List<TaxonNodeAgentRelation> records = null;\r
\r
+         long count = dao.countTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid);\r
+         if(PagerUtils.hasResultsInRange(count, pageIndex, pageSize)) {\r
+             records = dao.listTaxonNodeAgentRelations(taxonUuid, classificationUuid,\r
+                     agentUuid, rankUuid, relTypeUuid, PagerUtils.startFor(pageSize, pageIndex), PagerUtils.limitFor(pageSize), propertyPaths);\r
+         }\r
\r
+         Pager<TaxonNodeAgentRelation> pager = new DefaultPagerImpl<TaxonNodeAgentRelation>(pageIndex, count, pageSize, records);\r
+         return pager;\r
+     }\r
\r
+ }\r
index 0e93d8c2f1943fbad8f3e58c23645daa9ddee324,7e82efbac92807422693ad8c8c7f25b46fb7b18e..33efe32a9d9a872774e5c2104b4503356f4ca7bb
- // $Id$\r
- /**\r
- * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy\r
- * http://www.e-taxonomy.eu\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version 1.1\r
- * See LICENSE.TXT at the top of this package for the full license terms.\r
- */\r
\r
- package eu.etaxonomy.cdm.api.service;\r
\r
- import java.io.IOException;\r
- import java.util.ArrayList;\r
- import java.util.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.commons.lang.StringUtils;\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.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.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.NodeDeletionConfigurator.ChildHandling;\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.dto.FindByIdentifierDTO;\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.Annotation;\r
- import eu.etaxonomy.cdm.model.common.AnnotationType;\r
- import eu.etaxonomy.cdm.model.common.CdmBase;\r
- import eu.etaxonomy.cdm.model.common.DefinedTerm;\r
- import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
- 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.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.PresenceAbsenceTerm;\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.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.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
- import eu.etaxonomy.cdm.model.taxon.Taxon;\r
- import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
- import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
- import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
- import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\r
- import eu.etaxonomy.cdm.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.dao.taxon.ITaxonNodeDao;\r
- import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;\r
- import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;\r
- import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
- import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
- import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;\r
- import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;\r
\r
\r
- /**\r
-  * @author a.kohlbecker\r
-  * @date 10.09.2010\r
-  *\r
-  */\r
- @Service\r
- @Transactional(readOnly = true)\r
- public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{\r
-     private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);\r
\r
-     public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";\r
\r
-     public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";\r
\r
-     public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";\r
\r
-     @Autowired\r
-     private ITaxonNodeDao taxonNodeDao;\r
\r
-     @Autowired\r
-     private ITaxonNameDao nameDao;\r
\r
-     @Autowired\r
-     private INameService nameService;\r
\r
-     @Autowired\r
-     private IOccurrenceService occurrenceService;\r
\r
-     @Autowired\r
-     private ITaxonNodeService nodeService;\r
\r
-     @Autowired\r
-     private ICdmGenericDao genericDao;\r
\r
-     @Autowired\r
-     private IDescriptionService descriptionService;\r
\r
-     @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
-     public TaxonServiceImpl(){\r
-         if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }\r
-     }\r
\r
-     /**\r
-      * 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
-      * 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
-         }\r
-         return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);\r
-     }\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
-     @Override\r
-     public List<RelationshipBase> getAllRelationships(int limit, int start){\r
-         return dao.getAllRelationships(limit, start);\r
-     }\r
\r
-     /**\r
-      * FIXME Candidate for harmonization\r
-      * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?\r
-      */\r
-     @Override\r
-     @Deprecated\r
-     public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {\r
\r
-         String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";\r
-         UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);\r
-         OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =\r
-             (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);\r
-         return taxonRelTypeVocabulary;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){\r
-       UpdateResult result = new UpdateResult();\r
-         TaxonNameBase<?,?> synonymName = synonym.getName();\r
-         synonymName.removeTaxonBase(synonym);\r
-         TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();\r
-         taxonName.removeTaxonBase(acceptedTaxon);\r
\r
-         synonym.setName(taxonName);\r
-         acceptedTaxon.setName(synonymName);\r
-         saveOrUpdate(synonym);\r
-         saveOrUpdate(acceptedTaxon);\r
-         result.addUpdatedObject(acceptedTaxon);\r
-         result.addUpdatedObject(synonym);\r
-               return result;\r
\r
-         // the accepted taxon needs a new uuid because the concept has changed\r
-         // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"\r
-         //acceptedTaxon.setUuid(UUID.randomUUID());\r
-     }\r
\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{\r
\r
-         TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();\r
-         TaxonNameBase<?,?> synonymName = synonym.getName();\r
-         HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();\r
\r
-         //check synonym is not homotypic\r
-         if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){\r
-             String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";\r
-             throw new HomotypicalGroupChangeException(message);\r
-         }\r
\r
-         Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());\r
-         dao.save(newAcceptedTaxon);\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
-             }\r
-         }\r
-         dao.saveOrUpdate(acceptedTaxon);\r
-         //synonym.getName().removeTaxonBase(synonym);\r
\r
-         if (deleteSynonym){\r
- //                    deleteSynonym(synonym, taxon, false);\r
-             try {\r
-                 this.dao.flush();\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
-             }\r
-         }\r
\r
-         return newAcceptedTaxon;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public UpdateResult changeSynonymToAcceptedTaxon(UUID synonymUuid,\r
-             UUID acceptedTaxonUuid,\r
-             UUID newParentNodeUuid,\r
-             boolean deleteSynonym,\r
-             boolean copyCitationInfo,\r
-             Reference citation,\r
-             String microCitation) throws HomotypicalGroupChangeException {\r
-         UpdateResult result = new UpdateResult();\r
-         Synonym synonym = CdmBase.deproxy(dao.load(synonymUuid), Synonym.class);\r
-         Taxon acceptedTaxon = CdmBase.deproxy(dao.load(acceptedTaxonUuid), Taxon.class);\r
-         Taxon taxon =  changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, deleteSynonym, copyCitationInfo, citation, microCitation);\r
-         TaxonNode newParentNode = taxonNodeDao.load(newParentNodeUuid);\r
-         TaxonNode newNode = newParentNode.addChildTaxon(taxon, null, null);\r
-         taxonNodeDao.save(newNode);\r
-         result.addUpdatedObject(taxon);\r
-         result.addUpdatedObject(acceptedTaxon);\r
-         result.setCdmEntity(newNode);\r
-         return result;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public UpdateResult changeSynonymToRelatedTaxon(UUID synonymUuid,\r
-             UUID toTaxonUuid,\r
-             TaxonRelationshipType taxonRelationshipType,\r
-             Reference citation,\r
-             String microcitation){\r
\r
-         UpdateResult result = new UpdateResult();\r
-         Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);\r
-         Synonym synonym = (Synonym) dao.load(synonymUuid);\r
-         Taxon relatedTaxon = changeSynonymToRelatedTaxon(synonym, toTaxon, taxonRelationshipType, citation, microcitation);\r
-         result.setCdmEntity(relatedTaxon);\r
-         result.addUpdatedObject(relatedTaxon);\r
-         result.addUpdatedObject(toTaxon);\r
-         return result;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){\r
\r
-         // Get name from synonym\r
-         TaxonNameBase<?, ?> synonymName = synonym.getName();\r
\r
-       /*  // remove synonym from taxon\r
-         toTaxon.removeSynonym(synonym);\r
- */\r
-         // Create a taxon with synonym name\r
-         Taxon fromTaxon = Taxon.NewInstance(synonymName, null);\r
\r
-         // Add taxon relation\r
-         fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);\r
\r
-         // since we are swapping names, we have to detach the name from the synonym completely.\r
-         // Otherwise the synonym will still be in the list of typified names.\r
-        // synonym.getName().removeTaxonBase(synonym);\r
-         this.deleteSynonym(synonym, null);\r
\r
-         return fromTaxon;\r
-     }\r
\r
-     @Transactional(readOnly = false)\r
-     @Override\r
-     public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,\r
-                         boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){\r
-         // Get synonym name\r
-         TaxonNameBase synonymName = synonym.getName();\r
-         HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();\r
\r
\r
-         // Switch groups\r
-         oldHomotypicalGroup.removeTypifiedName(synonymName, false);\r
-         newHomotypicalGroup.addTypifiedName(synonymName);\r
\r
-         //remove existing basionym relationships\r
-         synonymName.removeBasionyms();\r
\r
-         //add basionym relationship\r
-         if (setBasionymRelationIfApplicable){\r
-             Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();\r
-             for (TaxonNameBase basionym : basionyms){\r
-                 synonymName.addBasionym(basionym);\r
-             }\r
-         }\r
\r
-         //set synonym relationship correctly\r
- //                    SynonymRelationship relToTaxon = null;\r
-         boolean relToTargetTaxonExists = false;\r
-         Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();\r
-         for (SynonymRelationship rel : existingRelations){\r
-             Taxon acceptedTaxon = rel.getAcceptedTaxon();\r
-             boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);\r
-             HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();\r
-             boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);\r
-             SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
-             rel.setType(newRelationType);\r
-             //TODO handle citation and microCitation\r
\r
-             if (isTargetTaxon){\r
-                 relToTargetTaxonExists = true;\r
-             }else{\r
-                 if (removeFromOtherTaxa){\r
-                     acceptedTaxon.removeSynonym(synonym, false);\r
-                 }else{\r
-                     //do nothing\r
-                 }\r
-             }\r
-         }\r
-         if (targetTaxon != null &&  ! relToTargetTaxonExists ){\r
-             Taxon acceptedTaxon = targetTaxon;\r
-             HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();\r
-             boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);\r
-             SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
-             //TODO handle citation and microCitation\r
-             Reference citation = null;\r
-             String microCitation = null;\r
-             acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);\r
-         }\r
\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {\r
-         if (clazz == null){\r
-             clazz = TaxonBase.class;\r
-         }\r
-         super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);\r
-     }\r
\r
-     @Override\r
-     @Autowired\r
-     protected void setDao(ITaxonDao dao) {\r
-         this.dao = dao;\r
-     }\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
-         List<TaxonBase> results = new ArrayList<TaxonBase>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);\r
-         }\r
\r
-         return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @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
-         List<TaxonBase> results = new ArrayList<TaxonBase>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);\r
-         }\r
\r
-         return results;\r
-     }\r
\r
-     @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
-         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);\r
-         }\r
-         return results;\r
-     }\r
\r
-     @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
-         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);\r
-         }\r
-         return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @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
-         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);\r
-         }\r
-         return results;\r
-     }\r
\r
-     @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
-         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);\r
-         }\r
-         return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @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
-     @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
-     @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
-         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-         }\r
\r
-         return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @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
-         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);\r
-         }\r
\r
-         return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);\r
-     }\r
\r
-     @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
-     @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
-     }\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
-         List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());\r
-         for(HomotypicalGroup homotypicalGroup : homotypicalGroups){\r
-             heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));\r
-         }\r
-         return heterotypicSynonymyGroups;\r
-     }\r
\r
-     @Override\r
-     public List<UuidAndTitleCache<IdentifiableEntity>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){\r
\r
-         List<UuidAndTitleCache<IdentifiableEntity>> results = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>();\r
\r
\r
-         if (configurator.isDoSynonyms() || configurator.isDoTaxa() || configurator.isDoNamesWithoutTaxa()){\r
-               results = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.isDoNamesWithoutTaxa(), configurator.isDoMisappliedNames(),configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
-         }\r
-         if (configurator.isDoTaxaByCommonNames()) {\r
-             //if(configurator.getPageSize() == null ){\r
-                 List<UuidAndTitleCache<IdentifiableEntity>> commonNameResults = dao.getTaxaByCommonNameForEditor(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
-                 if(commonNameResults != null){\r
-                     results.addAll(commonNameResults);\r
-                 }\r
-            // }\r
-         }\r
-         return results;\r
-     }\r
\r
-     @Override\r
-     public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {\r
\r
-         List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();\r
-         int numberOfResults = 0; // overall number of results (as opposed to number of results per page)\r
-         List<TaxonBase> taxa = null;\r
\r
-         // Taxa and synonyms\r
-         long numberTaxaResults = 0L;\r
\r
\r
-         List<String> propertyPath = new ArrayList<String>();\r
-         if(configurator.getTaxonPropertyPath() != null){\r
-             propertyPath.addAll(configurator.getTaxonPropertyPath());\r
-         }\r
\r
\r
-        if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){\r
-             if(configurator.getPageSize() != null){ // no point counting if we need all anyway\r
-                 numberTaxaResults =\r
-                     dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),\r
-                         configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),\r
-                         configurator.getNamedAreas());\r
-             }\r
\r
-             if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results\r
-                 taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),\r
-                     configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),\r
-                     configurator.getMatchMode(), configurator.getNamedAreas(),\r
-                     configurator.getPageSize(), configurator.getPageNumber(), propertyPath);\r
-             }\r
-        }\r
\r
-         if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }\r
\r
-         if(taxa != null){\r
-             results.addAll(taxa);\r
-         }\r
\r
-         numberOfResults += numberTaxaResults;\r
\r
-         // Names without taxa\r
-         if (configurator.isDoNamesWithoutTaxa()) {\r
-             int numberNameResults = 0;\r
\r
-             List<? extends TaxonNameBase<?,?>> names =\r
-                 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),\r
-                         configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());\r
-             if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }\r
-             if (names.size() > 0) {\r
-                 for (TaxonNameBase<?,?> taxonName : names) {\r
-                     if (taxonName.getTaxonBases().size() == 0) {\r
-                         results.add(taxonName);\r
-                         numberNameResults++;\r
-                     }\r
-                 }\r
-                 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }\r
-                 numberOfResults += numberNameResults;\r
-             }\r
-         }\r
\r
-         // Taxa from common names\r
\r
-         if (configurator.isDoTaxaByCommonNames()) {\r
-             taxa = new ArrayList<TaxonBase>();\r
-             numberTaxaResults = 0;\r
-             if(configurator.getPageSize() != null){// no point counting if we need all anyway\r
-                 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());\r
-             }\r
-             if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){\r
-                 List<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
-             }\r
-             numberOfResults += numberTaxaResults;\r
\r
-         }\r
\r
-        return new DefaultPagerImpl<IdentifiableEntity>\r
-             (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);\r
-     }\r
\r
-     public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){\r
-         return dao.getUuidAndTitleCache();\r
-     }\r
\r
-     @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
-         Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
-         for (TaxonDescription taxDesc: descriptions){\r
-             Set<DescriptionElementBase> elements = taxDesc.getElements();\r
-             for (DescriptionElementBase descElem: elements){\r
-                 for(Media media : descElem.getMedia()){\r
\r
-                     //find the best matching representation\r
-                     medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));\r
\r
-                 }\r
-             }\r
-         }\r
-         return medRep;\r
-     }\r
\r
-     @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
-     @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.setLevel(Level.TRACE);\r
- //        Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);\r
\r
-         logger.trace("listMedia() - START");\r
\r
-         Set<Taxon> taxa = new HashSet<Taxon>();\r
-         List<Media> taxonMedia = new ArrayList<Media>();\r
-         List<Media> nonImageGalleryImages = new ArrayList<Media>();\r
\r
-         if (limitToGalleries == null) {\r
-             limitToGalleries = false;\r
-         }\r
\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
-             }\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
-                 if (occurrence.isInstanceOf(DerivedUnit.class)) {\r
-                     DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);\r
-                     // Collection\r
-                     //TODO why may collections have media attached? #\r
-                     if (derivedUnit.getCollection() != null){\r
-                         taxonMedia.addAll(derivedUnit.getCollection().getMedia());\r
-                     }\r
-                 }\r
-                 //media in hierarchy\r
-                 taxonMedia.addAll(occurrenceService.getMediainHierarchy(occurrence, null, null, propertyPath).getRecords());\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
\r
-         logger.trace("listMedia() - initialize");\r
-         beanInitializer.initializeAll(taxonMedia, propertyPath);\r
\r
-         logger.trace("listMedia() - END");\r
\r
-         return taxonMedia;\r
-     }\r
\r
-     @Override\r
-     public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {\r
-         return this.dao.listByIds(listOfIDs, null, null, null, null);\r
-     }\r
\r
-     @Override\r
-     public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){\r
-         return this.dao.findByUuid(uuid, null ,propertyPaths);\r
-     }\r
\r
-     @Override\r
-     public int countAllRelationships() {\r
-         return this.dao.countAllRelationships();\r
-     }\r
\r
-     @Override\r
-     public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {\r
-         return this.dao.findIdenticalTaxonNames(propertyPath);\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult deleteTaxon(UUID taxonUUID, TaxonDeletionConfigurator config, UUID classificationUuid)  {\r
\r
-       if (config == null){\r
-             config = new TaxonDeletionConfigurator();\r
-         }\r
-       Taxon taxon = (Taxon)dao.load(taxonUUID);\r
-       DeleteResult result = new DeleteResult();\r
-       if (taxon == null){\r
-           result.setAbort();\r
-           result.addException(new Exception ("The taxon was already deleted."));\r
-           return result;\r
-       }\r
-       taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);\r
-       Classification classification = HibernateProxyHelper.deproxy(classificationDao.load(classificationUuid), Classification.class);\r
-         result = isDeletable(taxon, config);\r
\r
-         if (result.isOk()){\r
-             // --- DeleteSynonymRelations\r
-             if (config.isDeleteSynonymRelations()){\r
-                 boolean removeSynonymNameFromHomotypicalGroup = false;\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
-                         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
\r
-                 }\r
-             }\r
\r
-             // --- DeleteTaxonRelationships\r
-             if (! config.isDeleteTaxonRelationships()){\r
-                 if (taxon.getTaxonRelations().size() > 0){\r
-                     result.setAbort();\r
-                     result.addException(new Exception("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
\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
\r
-                                 this.deleteTaxon(taxRel.getFromTaxon().getUuid(), config, classificationUuid);\r
-                             }\r
-                         }\r
-                     }\r
-                     taxon.removeTaxonRelation(taxRel);\r
\r
-                 }\r
-             }\r
\r
-             //        TaxonDescription\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
-                         result.setAbort();\r
-                         result.addException(new Exception("Taxon can't be deleted as it is used in a TaxonDescription" +\r
-                                 " which also describes specimens or observations"));\r
-                         break;\r
-                     }\r
-                     removeDescriptions.add(desc);\r
\r
\r
-                 }\r
-                 if (result.isOk()){\r
-                     for (TaxonDescription desc: removeDescriptions){\r
-                         taxon.removeDescription(desc);\r
-                         descriptionService.delete(desc);\r
-                     }\r
-                 } else {\r
-                     return result;\r
-                 }\r
-             }\r
\r
\r
-          if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null && taxon.getTaxonNodes().size() > 1)){\r
-                 //if (taxon.getTaxonNodes().size() > 0){\r
-                  result.addException(new Exception( "Taxon can't be deleted as it is used in more than one classification."));\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
-                             HibernateProxyHelper.deproxy(node, TaxonNode.class);\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
-                         List<TaxonNode> nodesList = new ArrayList<TaxonNode>();\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
-                                     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
-                         config.getTaxonNodeConfig().setDeleteElement(false);\r
-                         DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);\r
-                         if (!resultNodes.isOk()){\r
-                               result.addExceptions(resultNodes.getExceptions());\r
-                               result.setStatus(resultNodes.getStatus());\r
-                         } else {\r
-                             result.addUpdatedObjects(resultNodes.getUpdatedObjects());\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
-             //TaxonNameBase\r
-             if (config.isDeleteNameIfPossible() && result.isOk()){\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
\r
-                     if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && name != null ){\r
\r
-                         //name.removeTaxonBase(taxon);\r
-                         //nameService.saveOrUpdate(name);\r
-                         taxon.setName(null);\r
-                         //dao.delete(taxon);\r
-                         DeleteResult nameResult = new DeleteResult();\r
\r
-                         //remove name if possible (and required)\r
-                         if (name != null && config.isDeleteNameIfPossible()){\r
-                               nameResult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());\r
-                         }\r
\r
-                         if (nameResult.isError() || nameResult.isAbort()){\r
-                               //result.setError();\r
-                               result.addRelatedObject(name);\r
-                               result.addExceptions(nameResult.getExceptions());\r
-                         }\r
\r
-                     }\r
\r
-             }else {\r
-                 taxon.setName(null);\r
-             }\r
\r
-             if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0)  && result.isOk()){\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 because it is used in a classification."));\r
\r
-             }\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
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult delete(UUID synUUID){\r
-       DeleteResult result = new DeleteResult();\r
-       Synonym syn = (Synonym)dao.load(synUUID);\r
\r
-         return this.deleteSynonym(syn, null);\r
-     }\r
\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {\r
-         return deleteSynonym(synonym, null, config);\r
\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public DeleteResult deleteSynonym(UUID synonymUuid, SynonymDeletionConfigurator config) {\r
-         return deleteSynonym((Synonym)dao.load(synonymUuid), config);\r
\r
-     }\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
-               result.addException(new Exception("The synonym was already deleted."));\r
-               return result;\r
-         }\r
\r
-         if (config == null){\r
-             config = new SynonymDeletionConfigurator();\r
-         }\r
\r
-         result = isDeletable(synonym, config);\r
\r
\r
-         if (result.isOk()){\r
\r
-             synonym = HibernateProxyHelper.deproxy(this.load(synonym.getUuid()), 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
-               relatedTaxon = HibernateProxyHelper.deproxy(relatedTaxon, Taxon.class);\r
-                 relatedTaxon.removeSynonym(synonym, false);\r
-                 this.saveOrUpdate(relatedTaxon);\r
-             }\r
-             this.saveOrUpdate(synonym);\r
\r
-             //TODO remove name from homotypical group?\r
\r
-             //remove synonym (if necessary)\r
\r
-             result.addUpdatedObject(taxon);\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 && config.isDeleteNameIfPossible()){\r
\r
-                         DeleteResult nameDeleteresult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());\r
-                         if (nameDeleteresult.isAbort()){\r
-                               result.addExceptions(nameDeleteresult.getExceptions());\r
-                               result.addRelatedObject(name);\r
-                         }\r
\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
\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
-     @Override\r
-     public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {\r
\r
-         return this.dao.findIdenticalNamesNew(propertyPath);\r
-     }\r
\r
-     @Override\r
-     public String getPhylumName(TaxonNameBase name){\r
-         return this.dao.getPhylumName(name);\r
-     }\r
\r
-     @Override\r
-     public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {\r
-         return dao.deleteSynonymRelationships(syn, taxon);\r
-     }\r
\r
-     @Override\r
-     public long deleteSynonymRelationships(Synonym syn) {\r
-         return dao.deleteSynonymRelationships(syn, null);\r
-     }\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
-         Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);\r
\r
-         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-             results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);\r
-         }\r
-         return results;\r
-     }\r
\r
-     @Override\r
-     public Taxon findBestMatchingTaxon(String taxonName) {\r
-         MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();\r
-         config.setTaxonNameTitle(taxonName);\r
-         return findBestMatchingTaxon(config);\r
-     }\r
\r
-     @Override\r
-     public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {\r
\r
-         Taxon bestCandidate = null;\r
-         try{\r
-             // 1. search for acceptet taxa\r
-             List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);\r
-             boolean bestCandidateMatchesSecUuid = false;\r
-             boolean bestCandidateIsInClassification = false;\r
-             int countEqualCandidates = 0;\r
-             for(TaxonBase taxonBaseCandidate : taxonList){\r
-                 if(taxonBaseCandidate instanceof Taxon){\r
-                     Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);\r
-                     boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);\r
-                     if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){\r
-                         continue;\r
-                     }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){\r
-                         bestCandidate = newCanditate;\r
-                         countEqualCandidates = 1;\r
-                         bestCandidateMatchesSecUuid = true;\r
-                         continue;\r
-                     }\r
\r
-                     boolean newCandidateInClassification = isInClassification(newCanditate, config);\r
-                     if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){\r
-                         continue;\r
-                     }else if (newCandidateInClassification && ! bestCandidateIsInClassification){\r
-                         bestCandidate = newCanditate;\r
-                         countEqualCandidates = 1;\r
-                         bestCandidateIsInClassification = true;\r
-                         continue;\r
-                     }\r
-                     if (bestCandidate == null){\r
-                         bestCandidate = newCanditate;\r
-                         countEqualCandidates = 1;\r
-                         continue;\r
-                     }\r
\r
-                 }else{  //not Taxon.class\r
-                     continue;\r
-                 }\r
-                 countEqualCandidates++;\r
\r
-             }\r
-             if (bestCandidate != null){\r
-                 if(countEqualCandidates > 1){\r
-                     logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());\r
-                     return bestCandidate;\r
-                 } else {\r
-                     logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());\r
-                     return bestCandidate;\r
-                 }\r
-             }\r
\r
\r
-             // 2. search for synonyms\r
-             if (config.isIncludeSynonyms()){\r
-                 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);\r
-                 for(TaxonBase taxonBase : synonymList){\r
-                     if(taxonBase instanceof Synonym){\r
-                         Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);\r
-                         Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();\r
-                         if(!acceptetdCandidates.isEmpty()){\r
-                             bestCandidate = acceptetdCandidates.iterator().next();\r
-                             if(acceptetdCandidates.size() == 1){\r
-                                 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());\r
-                                 return bestCandidate;\r
-                             } else {\r
-                                 logger.info("using accepted Taxon " +  bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());\r
-                                 return bestCandidate;\r
-                             }\r
-                             //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found\r
-                         }\r
-                     }\r
-                 }\r
-             }\r
\r
-         } catch (Exception e){\r
-             logger.error(e);\r
-             e.printStackTrace();\r
-         }\r
\r
-         return bestCandidate;\r
-     }\r
\r
-     private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {\r
-         UUID configClassificationUuid = config.getClassificationUuid();\r
-         if (configClassificationUuid == null){\r
-             return false;\r
-         }\r
-         for (TaxonNode node : taxon.getTaxonNodes()){\r
-             UUID classUuid = node.getClassification().getUuid();\r
-             if (configClassificationUuid.equals(classUuid)){\r
-                 return true;\r
-             }\r
-         }\r
-         return false;\r
-     }\r
\r
-     private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {\r
-         UUID configSecUuid = config.getSecUuid();\r
-         if (configSecUuid == null){\r
-             return false;\r
-         }\r
-         UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();\r
-         return configSecUuid.equals(taxonSecUuid);\r
-     }\r
\r
-     @Override\r
-     public Synonym findBestMatchingSynonym(String taxonName) {\r
-         List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);\r
-         if(! synonymList.isEmpty()){\r
-             Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);\r
-             if(synonymList.size() == 1){\r
-                 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );\r
-                 return result;\r
-             } else {\r
-                 logger.info("Several matching synonyms found. Using first: " +  result.getTitleCache());\r
-                 return result;\r
-             }\r
-         }\r
-         return null;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\r
-     public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation,\r
-             Taxon newTaxon,\r
-             boolean moveHomotypicGroup,\r
-             SynonymRelationshipType newSynonymRelationshipType,\r
-             Reference reference,\r
-             String referenceDetail,\r
-             boolean keepReference) throws HomotypicalGroupChangeException {\r
\r
-         Synonym synonym = (Synonym) dao.load(oldSynonymRelation.getSynonym().getUuid());\r
-         Taxon fromTaxon = (Taxon) dao.load(oldSynonymRelation.getAcceptedTaxon().getUuid());\r
-         //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)\r
-         TaxonNameBase<?,?> synonymName = synonym.getName();\r
-         TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();\r
-         //set default relationship type\r
-         if (newSynonymRelationshipType == null){\r
-             newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();\r
-         }\r
-         boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());\r
\r
-         HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();\r
-         int hgSize = homotypicGroup.getTypifiedNames().size();\r
-         boolean isSingleInGroup = !(hgSize > 1);\r
\r
-         if (! isSingleInGroup){\r
-             boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);\r
-             boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;\r
-             if (isHomotypicToAccepted){\r
-                 String message = "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";\r
-                 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";\r
-                 message = String.format(message, homotypicRelatives);\r
-                 throw new HomotypicalGroupChangeException(message);\r
-             }\r
-             if (! moveHomotypicGroup){\r
-                 String message = "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";\r
-                 throw new HomotypicalGroupChangeException(message);\r
-             }\r
-         }else{\r
-             moveHomotypicGroup = true;  //single synonym always allows to moveCompleteGroup\r
-         }\r
- //        Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);\r
\r
-         SynonymRelationship result = null;\r
-         //move all synonyms to new taxon\r
-         List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);\r
-         for (Synonym syn: homotypicSynonyms){\r
-             Set<SynonymRelationship> synRelations = syn.getSynonymRelations();\r
-             for (SynonymRelationship synRelation : synRelations){\r
-                 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){\r
-                     Reference<?> newReference = reference;\r
-                     if (newReference == null && keepReference){\r
-                         newReference = synRelation.getCitation();\r
-                     }\r
-                     String newRefDetail = referenceDetail;\r
-                     if (newRefDetail == null && keepReference){\r
-                         newRefDetail = synRelation.getCitationMicroReference();\r
-                     }\r
-                     newTaxon = HibernateProxyHelper.deproxy(newTaxon, Taxon.class);\r
-                     fromTaxon = HibernateProxyHelper.deproxy(fromTaxon, Taxon.class);\r
-                     SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);\r
-                     fromTaxon.removeSynonymRelation(synRelation, false);\r
- //\r
-                     //change homotypic group of synonym if relType is 'homotypic'\r
- //                    if (newRelTypeIsHomotypic){\r
- //                            newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());\r
- //                    }\r
-                     //set result\r
-                     if (synRelation.equals(oldSynonymRelation)){\r
-                         result = newSynRelation;\r
-                     }\r
-                 }\r
-             }\r
\r
-         }\r
-         saveOrUpdate(fromTaxon);\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
-     @Override\r
-     public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {\r
-         return dao.getUuidAndTitleCacheTaxon();\r
-     }\r
\r
-     @Override\r
-     public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {\r
-         return dao.getUuidAndTitleCacheSynonym();\r
-     }\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<PresenceAbsenceTerm> 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
-         if(!queryString.isEmpty() && !queryString.equals("*") && !queryString.equals("?") ) {\r
-             textQuery.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
-             textQuery.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);\r
-         }\r
\r
-         if(textQuery.getClauses().length > 0) {\r
-             finalQuery.add(textQuery, Occur.MUST);\r
-         }\r
\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 luceneSearch;\r
-     }\r
\r
-     @Override\r
-     public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(\r
-             EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,\r
-             Set<NamedArea> namedAreas, Set<PresenceAbsenceTerm> 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<PresenceAbsenceTerm>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<PresenceAbsenceTerm>(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
-         // 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
\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
-           ======== 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, multiSearch.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 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<PresenceAbsenceTerm> 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<PresenceAbsenceTerm> 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<PresenceAbsenceTerm> 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
-     @Override\r
-     public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(\r
-             Class<? extends DescriptionElementBase> clazz, String queryString,\r
-             Classification classification, List<Feature> features, 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 = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);\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.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
\r
-         // --- initialize taxa, highlight matches ....\r
-         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
-         @SuppressWarnings("rawtypes")\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
\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, LuceneMultiSearchException {\r
\r
-         LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
-         LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null);\r
\r
-         LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\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
-         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
-         idFieldMap.put(CdmBaseType.TAXON, "id");\r
-         idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
\r
-         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
-                 topDocsResultSet, multiSearch.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
\r
-     /**\r
-      * @param clazz\r
-      * @param queryString\r
-      * @param classification\r
-      * @param features\r
-      * @param languages\r
-      * @param highlightFragments\r
-      * @param directorySelectClass\r
-      * @return\r
-      */\r
-     protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,\r
-             String queryString, Classification classification, List<Feature> features,\r
-             List<Language> languages, boolean highlightFragments) {\r
\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
-         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 = descriptionElementQueryFactory.newTermQuery("name", queryString);\r
-         } else {\r
-             nameQuery = new BooleanQuery();\r
-             BooleanQuery languageSubQuery = new BooleanQuery();\r
-             for(Language lang : languages){\r
-                 languageSubQuery.add(descriptionElementQueryFactory.newTermQuery("language.uuid",  lang.getUuid().toString(), false), Occur.SHOULD);\r
-             }\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(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
\r
-         // --- TermBase fields - by representation ----\r
-         // state field from CategoricalData\r
-         textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);\r
\r
-         // state field from CategoricalData\r
-         textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);\r
\r
-         // area field from Distribution\r
-         textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
\r
-         // status field from Distribution\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(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(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);\r
-         }\r
\r
-         // the description must be associated with a taxon\r
-         finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
\r
-         logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());\r
-         return finalQuery;\r
-     }\r
\r
-     /**\r
-      * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}\r
-      * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.\r
-      * This method is a convenient means to retrieve a Lucene query string for such the fields.\r
-      *\r
-      * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}\r
-      * or {@link MultilanguageTextFieldBridge }\r
-      * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages\r
-      * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned\r
-      * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.\r
-      *\r
-      * TODO move to utiliy class !!!!!!!!\r
-      */\r
-     private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {\r
\r
-         if(stringBuilder == null){\r
-             stringBuilder = new StringBuilder();\r
-         }\r
-         if(languages == null || languages.size() == 0){\r
-             stringBuilder.append(name + ".ALL:(%1$s) ");\r
-         } else {\r
-             for(Language lang : languages){\r
-                 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");\r
-             }\r
-         }\r
-         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
\r
-         HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();\r
\r
\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
-         if (taxonName.isSpecies()){\r
-              epithetOfTaxon= taxonName.getSpecificEpithet();\r
-         } else if (taxonName.isInfraGeneric()){\r
-             infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();\r
-         } else if (taxonName.isInfraSpecific()){\r
-             infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();\r
-         }\r
-         String genusOfTaxon = taxonName.getGenusOrUninomial();\r
-         Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
-         List<String> taxonNames = new ArrayList<String>();\r
\r
-         for (TaxonNode node: nodes){\r
-            // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName\r
-            // List<String> synonymsEpithet = new ArrayList<String>();\r
\r
-             if (node.getClassification().equals(classification)){\r
-                 if (!node.isTopmostNode()){\r
-                     TaxonNode parent = node.getParent();\r
-                     parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);\r
-                     TaxonNameBase<?,?> parentName =  parent.getTaxon().getName();\r
-                     ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);\r
-                     Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());\r
-                     Rank rankOfTaxon = taxonName.getRank();\r
\r
\r
-                     //create inferred synonyms for species, subspecies\r
-                     if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){\r
\r
-                         Synonym inferredEpithet = null;\r
-                         Synonym inferredGenus = null;\r
-                         Synonym potentialCombination = null;\r
\r
-                         List<String> propertyPaths = new ArrayList<String>();\r
-                         propertyPaths.add("synonym");\r
-                         propertyPaths.add("synonym.name");\r
-                         List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
-                         orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));\r
\r
-                         List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
-                         List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
\r
-                         List<TaxonRelationship> taxonRelListParent = null;\r
-                         List<TaxonRelationship> taxonRelListTaxon = null;\r
-                         if (doWithMisappliedNames){\r
-                             taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
-                             taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
-                         }\r
\r
\r
-                         if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){\r
\r
\r
-                             for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
-                                 Synonym syn = synonymRelationOfParent.getSynonym();\r
\r
-                                 inferredEpithet = createInferredEpithets(taxon,\r
-                                         zooHashMap, taxonName, epithetOfTaxon,\r
-                                         infragenericEpithetOfTaxon,\r
-                                         infraspecificEpithetOfTaxon,\r
-                                         taxonNames, parentName,\r
-                                         syn);\r
\r
\r
-                                 inferredSynonyms.add(inferredEpithet);\r
-                                 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
-                                 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
-                             }\r
\r
-                             if (doWithMisappliedNames){\r
\r
-                                 for (TaxonRelationship taxonRelationship: taxonRelListParent){\r
-                                      Taxon misappliedName = taxonRelationship.getFromTaxon();\r
\r
-                                      inferredEpithet = createInferredEpithets(taxon,\r
-                                              zooHashMap, taxonName, epithetOfTaxon,\r
-                                              infragenericEpithetOfTaxon,\r
-                                              infraspecificEpithetOfTaxon,\r
-                                              taxonNames, parentName,\r
-                                              misappliedName);\r
\r
-                                     inferredSynonyms.add(inferredEpithet);\r
-                                     zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
-                                      taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
-                                 }\r
-                             }\r
\r
-                             if (!taxonNames.isEmpty()){\r
-                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
-                             ZoologicalName name;\r
-                             if (!synNotInCDM.isEmpty()){\r
-                                 inferredSynonymsToBeRemoved.clear();\r
\r
-                                 for (Synonym syn :inferredSynonyms){\r
-                                     name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
-                                     if (!synNotInCDM.contains(name.getNameCache())){\r
-                                         inferredSynonymsToBeRemoved.add(syn);\r
-                                     }\r
-                                 }\r
\r
-                                 // Remove identified Synonyms from inferredSynonyms\r
-                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
-                                     inferredSynonyms.remove(synonym);\r
-                                 }\r
-                             }\r
-                         }\r
\r
-                     }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){\r
\r
\r
-                         for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
-                             TaxonNameBase synName;\r
-                             ZoologicalName inferredSynName;\r
\r
-                             Synonym syn = synonymRelationOfTaxon.getSynonym();\r
-                             inferredGenus = createInferredGenus(taxon,\r
-                                     zooHashMap, taxonName, epithetOfTaxon,\r
-                                     genusOfTaxon, taxonNames, zooParentName, syn);\r
\r
-                             inferredSynonyms.add(inferredGenus);\r
-                             zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
-                             taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
\r
\r
-                         }\r
\r
-                         if (doWithMisappliedNames){\r
\r
-                             for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
-                                 Taxon misappliedName = taxonRelationship.getFromTaxon();\r
-                                 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);\r
\r
-                                 inferredSynonyms.add(inferredGenus);\r
-                                 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
-                                  taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
-                             }\r
-                         }\r
\r
\r
-                         if (!taxonNames.isEmpty()){\r
-                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
-                             ZoologicalName name;\r
-                             if (!synNotInCDM.isEmpty()){\r
-                                 inferredSynonymsToBeRemoved.clear();\r
\r
-                                 for (Synonym syn :inferredSynonyms){\r
-                                     name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
-                                     if (!synNotInCDM.contains(name.getNameCache())){\r
-                                         inferredSynonymsToBeRemoved.add(syn);\r
-                                     }\r
-                                 }\r
\r
-                                 // Remove identified Synonyms from inferredSynonyms\r
-                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
-                                     inferredSynonyms.remove(synonym);\r
-                                 }\r
-                             }\r
-                         }\r
\r
-                     }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){\r
\r
-                         Reference sourceReference = null; // TODO: Determination of sourceReference is redundant\r
-                         ZoologicalName inferredSynName;\r
-                         //for all synonyms of the parent...\r
-                         for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
-                             TaxonNameBase synName;\r
-                             Synonym synParent = synonymRelationOfParent.getSynonym();\r
-                             synName = synParent.getName();\r
\r
-                             HibernateProxyHelper.deproxy(synParent);\r
\r
-                             // Set the sourceReference\r
-                             sourceReference = synParent.getSec();\r
\r
-                             // Determine the idInSource\r
-                             String idInSourceParent = getIdInSource(synParent);\r
\r
-                             ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
-                             String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
-                             String synParentInfragenericName = null;\r
-                             String synParentSpecificEpithet = null;\r
\r
-                             if (parentSynZooName.isInfraGeneric()){\r
-                                 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
-                             }\r
-                             if (parentSynZooName.isSpecies()){\r
-                                 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
-                             }\r
\r
-                            /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
-                                 synonymsGenus.put(synGenusName, idInSource);\r
-                             }*/\r
\r
-                             //for all synonyms of the taxon\r
\r
-                             for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
\r
-                                 Synonym syn = synonymRelationOfTaxon.getSynonym();\r
-                                 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
-                                 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,\r
-                                         synParentGenus,\r
-                                         synParentInfragenericName,\r
-                                         synParentSpecificEpithet, syn, zooHashMap);\r
\r
-                                 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
-                                 inferredSynonyms.add(potentialCombination);\r
-                                 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
-                                  taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
\r
-                             }\r
\r
\r
-                         }\r
\r
-                         if (doWithMisappliedNames){\r
\r
-                             for (TaxonRelationship parentRelationship: taxonRelListParent){\r
\r
-                                 TaxonNameBase misappliedParentName;\r
\r
-                                 Taxon misappliedParent = parentRelationship.getFromTaxon();\r
-                                 misappliedParentName = misappliedParent.getName();\r
\r
-                                 HibernateProxyHelper.deproxy(misappliedParent);\r
\r
-                                 // Set the sourceReference\r
-                                 sourceReference = misappliedParent.getSec();\r
\r
-                                 // Determine the idInSource\r
-                                 String idInSourceParent = getIdInSource(misappliedParent);\r
\r
-                                 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);\r
-                                 String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
-                                 String synParentInfragenericName = null;\r
-                                 String synParentSpecificEpithet = null;\r
\r
-                                 if (parentSynZooName.isInfraGeneric()){\r
-                                     synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
-                                 }\r
-                                 if (parentSynZooName.isSpecies()){\r
-                                     synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
-                                 }\r
\r
\r
-                                 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
-                                     Taxon misappliedName = taxonRelationship.getFromTaxon();\r
-                                     ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);\r
-                                     potentialCombination = createPotentialCombination(\r
-                                             idInSourceParent, parentSynZooName, zooMisappliedName,\r
-                                             synParentGenus,\r
-                                             synParentInfragenericName,\r
-                                             synParentSpecificEpithet, misappliedName, zooHashMap);\r
\r
\r
-                                     taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
-                                     inferredSynonyms.add(potentialCombination);\r
-                                     zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
-                                      taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
-                                 }\r
-                             }\r
-                         }\r
\r
-                         if (!taxonNames.isEmpty()){\r
-                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
-                             ZoologicalName name;\r
-                             if (!synNotInCDM.isEmpty()){\r
-                                 inferredSynonymsToBeRemoved.clear();\r
-                                 for (Synonym syn :inferredSynonyms){\r
-                                     try{\r
-                                         name = (ZoologicalName) syn.getName();\r
-                                     }catch (ClassCastException e){\r
-                                         name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
-                                     }\r
-                                     if (!synNotInCDM.contains(name.getNameCache())){\r
-                                         inferredSynonymsToBeRemoved.add(syn);\r
-                                     }\r
-                                  }\r
-                                 // Remove identified Synonyms from inferredSynonyms\r
-                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
-                                     inferredSynonyms.remove(synonym);\r
-                                 }\r
-                             }\r
-                          }\r
-                         }\r
-                     }else {\r
-                         logger.info("The synonymrelationship type is not defined.");\r
-                         return inferredSynonyms;\r
-                     }\r
-                 }\r
-             }\r
\r
-         }\r
\r
-         return inferredSynonyms;\r
-     }\r
\r
-     private Synonym createPotentialCombination(String idInSourceParent,\r
-             ZoologicalName parentSynZooName,  ZoologicalName zooSynName, String synParentGenus,\r
-             String synParentInfragenericName, String synParentSpecificEpithet,\r
-             TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {\r
-         Synonym potentialCombination;\r
-         Reference sourceReference;\r
-         ZoologicalName inferredSynName;\r
-         HibernateProxyHelper.deproxy(syn);\r
\r
-         // Set sourceReference\r
-         sourceReference = syn.getSec();\r
-         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
-             if (!parentSynZooName.getTaxa().isEmpty()){\r
-                 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();\r
\r
-                 sourceReference = taxon.getSec();\r
-             }\r
-         }\r
-         String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();\r
\r
-         String synTaxonInfraSpecificName= null;\r
\r
-         if (parentSynZooName.isSpecies()){\r
-             synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();\r
-         }\r
\r
-         /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){\r
-             synonymsEpithet.add(epithetName);\r
-         }*/\r
\r
-         //create potential combinations...\r
-         inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());\r
\r
-         inferredSynName.setGenusOrUninomial(synParentGenus);\r
-         if (zooSynName.isSpecies()){\r
-               inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);\r
-               if (parentSynZooName.isInfraGeneric()){\r
-                   inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
-               }\r
-         }\r
-         if (zooSynName.isInfraSpecific()){\r
-             inferredSynName.setSpecificEpithet(synParentSpecificEpithet);\r
-             inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);\r
-         }\r
-         if (parentSynZooName.isInfraGeneric()){\r
-             inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
-         }\r
\r
\r
-         potentialCombination = Synonym.NewInstance(inferredSynName, null);\r
\r
-         // Set the sourceReference\r
-         potentialCombination.setSec(sourceReference);\r
\r
\r
-         // Determine the idInSource\r
-         String idInSourceSyn= getIdInSource(syn);\r
\r
-         if (idInSourceParent != null && idInSourceSyn != null) {\r
-             IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
-             inferredSynName.addSource(originalSource);\r
-             originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
-             potentialCombination.addSource(originalSource);\r
-         }\r
\r
-         return potentialCombination;\r
-     }\r
\r
-     private Synonym createInferredGenus(Taxon taxon,\r
-             HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
-             String epithetOfTaxon, String genusOfTaxon,\r
-             List<String> taxonNames, ZoologicalName zooParentName,\r
-             TaxonBase syn) {\r
\r
-         Synonym inferredGenus;\r
-         TaxonNameBase synName;\r
-         ZoologicalName inferredSynName;\r
-         synName =syn.getName();\r
-         HibernateProxyHelper.deproxy(syn);\r
\r
-         // Determine the idInSource\r
-         String idInSourceSyn = getIdInSource(syn);\r
-         String idInSourceTaxon = getIdInSource(taxon);\r
-         // Determine the sourceReference\r
-         Reference sourceReference = syn.getSec();\r
\r
-         //logger.warn(sourceReference.getTitleCache());\r
\r
-         synName = syn.getName();\r
-         ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
-         String synSpeciesEpithetName = synZooName.getSpecificEpithet();\r
-                      /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){\r
-             synonymsEpithet.add(synSpeciesEpithetName);\r
-         }*/\r
\r
-         inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
-         //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...\r
\r
\r
-         inferredSynName.setGenusOrUninomial(genusOfTaxon);\r
-         if (zooParentName.isInfraGeneric()){\r
-             inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());\r
-         }\r
\r
-         if (taxonName.isSpecies()){\r
-             inferredSynName.setSpecificEpithet(synSpeciesEpithetName);\r
-         }\r
-         if (taxonName.isInfraSpecific()){\r
-             inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
-             inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());\r
-         }\r
\r
\r
-         inferredGenus = Synonym.NewInstance(inferredSynName, null);\r
\r
-         // Set the sourceReference\r
-         inferredGenus.setSec(sourceReference);\r
\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
-             inferredGenus.addSource(originalSource);\r
\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
-             inferredGenus.addSource(originalSource);\r
\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
-         return inferredGenus;\r
-     }\r
\r
-     private Synonym createInferredEpithets(Taxon taxon,\r
-             HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
-             String epithetOfTaxon, String infragenericEpithetOfTaxon,\r
-             String infraspecificEpithetOfTaxon, List<String> taxonNames,\r
-             TaxonNameBase parentName, TaxonBase syn) {\r
\r
-         Synonym inferredEpithet;\r
-         TaxonNameBase<?,?> synName;\r
-         ZoologicalName inferredSynName;\r
-         HibernateProxyHelper.deproxy(syn);\r
\r
-         // Determine the idInSource\r
-         String idInSourceSyn = getIdInSource(syn);\r
-         String idInSourceTaxon =  getIdInSource(taxon);\r
-         // Determine the sourceReference\r
-         Reference<?> sourceReference = syn.getSec();\r
\r
-         if (sourceReference == null){\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
-         ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);\r
-         String synGenusName = zooSynName.getGenusOrUninomial();\r
-         String synInfraGenericEpithet = null;\r
-         String synSpecificEpithet = null;\r
\r
-         if (zooSynName.getInfraGenericEpithet() != null){\r
-             synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();\r
-         }\r
\r
-         if (zooSynName.isInfraSpecific()){\r
-             synSpecificEpithet = zooSynName.getSpecificEpithet();\r
-         }\r
\r
-                      /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
-             synonymsGenus.put(synGenusName, idInSource);\r
-         }*/\r
\r
-         inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
\r
-         // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!\r
-         if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {\r
-             logger.error("This specificEpithet is NULL" + taxon.getTitleCache());\r
-         }\r
-         inferredSynName.setGenusOrUninomial(synGenusName);\r
\r
-         if (parentName.isInfraGeneric()){\r
-             inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);\r
-         }\r
-         if (taxonName.isSpecies()){\r
-             inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
-         }else if (taxonName.isInfraSpecific()){\r
-             inferredSynName.setSpecificEpithet(synSpecificEpithet);\r
-             inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);\r
-         }\r
\r
-         inferredEpithet = Synonym.NewInstance(inferredSynName, null);\r
\r
-         // Set the sourceReference\r
-         inferredEpithet.setSec(sourceReference);\r
\r
-         /* Add the original source\r
-         if (idInSource != null) {\r
-             IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);\r
\r
-             // Add the citation\r
-             Reference citation = getCitation(syn);\r
-             if (citation != null) {\r
-                 originalSource.setCitation(citation);\r
-                 inferredEpithet.addSource(originalSource);\r
-             }\r
-         }*/\r
-         String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
\r
\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
\r
-         inferredSynName.addSource(originalSource);\r
\r
\r
\r
-         taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());\r
\r
-         return inferredEpithet;\r
-     }\r
\r
-     /**\r
-      * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.\r
-      * Very likely only useful for createInferredSynonyms().\r
-      * @param uuid\r
-      * @param zooHashMap\r
-      * @return\r
-      */\r
-     private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {\r
-         ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);\r
-         if (taxonName == null) {\r
-             taxonName = zooHashMap.get(uuid);\r
-         }\r
-         return taxonName;\r
-     }\r
\r
-     /**\r
-      * Returns the idInSource for a given Synonym.\r
-      * @param syn\r
-      */\r
-     private String getIdInSource(TaxonBase taxonBase) {\r
-         String idInSource = null;\r
-         Set<IdentifiableSource> sources = taxonBase.getSources();\r
-         if (sources.size() == 1) {\r
-             IdentifiableSource source = sources.iterator().next();\r
-             if (source != null) {\r
-                 idInSource  = source.getIdInSource();\r
-             }\r
-         } else if (sources.size() > 1) {\r
-             int count = 1;\r
-             idInSource = "";\r
-             for (IdentifiableSource source : sources) {\r
-                 idInSource += source.getIdInSource();\r
-                 if (count < sources.size()) {\r
-                     idInSource += "; ";\r
-                 }\r
-                 count++;\r
-             }\r
-         } else if (sources.size() == 0){\r
-             logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());\r
-         }\r
\r
\r
-         return idInSource;\r
-     }\r
\r
\r
-     /**\r
-      * Returns the citation for a given Synonym.\r
-      * @param syn\r
-      */\r
-     private Reference getCitation(Synonym syn) {\r
-         Reference citation = null;\r
-         Set<IdentifiableSource> sources = syn.getSources();\r
-         if (sources.size() == 1) {\r
-             IdentifiableSource source = sources.iterator().next();\r
-             if (source != null) {\r
-                 citation = source.getCitation();\r
-             }\r
-         } else if (sources.size() > 1) {\r
-             logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");\r
-         }\r
\r
-         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
-         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));\r
-         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));\r
-         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));\r
\r
-         return inferredSynonyms;\r
-     }\r
\r
-     @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
-     @Transactional(readOnly = false)\r
-     public UpdateResult changeRelatedTaxonToSynonym(UUID fromTaxonUuid,\r
-             UUID toTaxonUuid,\r
-             TaxonRelationshipType oldRelationshipType,\r
-             SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {\r
-         UpdateResult result = new UpdateResult();\r
-         Taxon fromTaxon = (Taxon) dao.load(fromTaxonUuid);\r
-         Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);\r
-         Synonym synonym = changeRelatedTaxonToSynonym(fromTaxon, toTaxon, oldRelationshipType, synonymRelationshipType);\r
-         result.setCdmEntity(synonym);\r
-         result.addUpdatedObject(fromTaxon);\r
-         result.addUpdatedObject(toTaxon);\r
-         result.addUpdatedObject(synonym);\r
\r
-         return result;\r
-     }\r
\r
-     @Override\r
-     @Transactional(readOnly = false)\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.getUuid(), config, null);\r
-                 return synonymRelationship.getSynonym();\r
\r
-     }\r
\r
-     @Override\r
-     public DeleteResult isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){\r
-         DeleteResult result = new DeleteResult();\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 DeleteResult isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){\r
-         String message;\r
-         DeleteResult result = new DeleteResult();\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.addException(new ReferencedObjectUndeletableException(message));\r
-                 result.addRelatedObject(ref);\r
-                 result.setAbort();\r
-             }\r
-         }\r
\r
-         return result;\r
-     }\r
\r
-     private DeleteResult isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){\r
-         String message = null;\r
-         DeleteResult result = new DeleteResult();\r
-         for (CdmBase ref: references){\r
-             if (!(ref instanceof TaxonNameBase)){\r
-               message = null;\r
-                 if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){\r
-                     message = "The taxon can't be deleted as long as it has synonyms.";\r
\r
-                 }\r
-                 if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){\r
-                     message = "The taxon can't be deleted as long as it has factual data.";\r
\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
\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
\r
-                     } else{\r
-                         message = "The taxon can't be deleted as long as it belongs to a taxon node.";\r
\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
\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 taxon";\r
\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
\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
\r
-                 }\r
\r
-             }\r
-             if (message != null){\r
-                   result.addException(new ReferencedObjectUndeletableException(message));\r
-                   result.addRelatedObject(ref);\r
-                   result.setAbort();\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, null);\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
\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
-       @Override\r
-       @Transactional(readOnly = true)\r
-       public <S extends TaxonBase> Pager<FindByIdentifierDTO<S>> findByIdentifier(\r
-                       Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,\r
-                       MatchMode matchmode, boolean includeEntity, Integer pageSize,\r
-                       Integer pageNumber,     List<String> propertyPaths) {\r
-               if (subtreeFilter == null){\r
-                       return findByIdentifier(clazz, identifier, identifierType, matchmode, includeEntity, pageSize, pageNumber, propertyPaths);\r
-               }\r
\r
-               Integer numberOfResults = dao.countByIdentifier(clazz, identifier, identifierType, subtreeFilter, matchmode);\r
-         List<Object[]> daoResults = new ArrayList<Object[]>();\r
-         if(numberOfResults > 0) { // no point checking again\r
-               daoResults = dao.findByIdentifier(clazz, identifier, identifierType, subtreeFilter,\r
-                               matchmode, includeEntity, pageSize, pageNumber, propertyPaths);\r
-         }\r
\r
-         List<FindByIdentifierDTO<S>> result = new ArrayList<FindByIdentifierDTO<S>>();\r
-         for (Object[] daoObj : daoResults){\r
-               if (includeEntity){\r
-                       result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (S)daoObj[2]));\r
-               }else{\r
-                       result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));\r
-               }\r
-         }\r
-               return new DefaultPagerImpl<FindByIdentifierDTO<S>>(pageNumber, numberOfResults, pageSize, result);\r
-       }\r
\r
-       @Override\r
-       @Transactional(readOnly = false)\r
-       public UpdateResult moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, UUID newTaxonUUID, boolean moveHomotypicGroup,\r
-             SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {\r
\r
-           UpdateResult result = new UpdateResult();\r
-               Taxon newTaxon = (Taxon) dao.load(newTaxonUUID);\r
-               SynonymRelationship sr = moveSynonymToAnotherTaxon(oldSynonymRelation, newTaxon, moveHomotypicGroup, newSynonymRelationshipType, reference, referenceDetail, keepReference);\r
-               result.setCdmEntity(sr);\r
-               result.addUpdatedObject(sr);\r
-               result.addUpdatedObject(newTaxon);\r
-               return result;\r
-       }\r
\r
-       @Override\r
-       public UpdateResult moveFactualDateToAnotherTaxon(UUID fromTaxonUuid, UUID toTaxonUuid){\r
-               UpdateResult result = new UpdateResult();\r
\r
-               Taxon fromTaxon = (Taxon)dao.load(fromTaxonUuid);\r
-               Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);\r
-                 for(TaxonDescription description : fromTaxon.getDescriptions()){\r
-               //reload to avoid session conflicts\r
-               description = HibernateProxyHelper.deproxy(description, TaxonDescription.class);\r
\r
-               String moveMessage = String.format("Description moved from %s", fromTaxon);\r
-               if(description.isProtectedTitleCache()){\r
-                   String separator = "";\r
-                   if(!StringUtils.isBlank(description.getTitleCache())){\r
-                       separator = " - ";\r
-                   }\r
-                   description.setTitleCache(description.getTitleCache() + separator + moveMessage, true);\r
-               }\r
-               Annotation annotation = Annotation.NewInstance(moveMessage, Language.getDefaultLanguage());\r
-               annotation.setAnnotationType(AnnotationType.TECHNICAL());\r
-               description.addAnnotation(annotation);\r
-               toTaxon.addDescription(description);\r
-               dao.saveOrUpdate(toTaxon);\r
-               dao.saveOrUpdate(fromTaxon);\r
-               result.addUpdatedObject(toTaxon);\r
-               result.addUpdatedObject(fromTaxon);\r
\r
-           }\r
\r
\r
-               return result;\r
-       }\r
\r
-       @Override\r
-       public DeleteResult deleteSynonym(UUID synonymUuid, UUID taxonUuid,\r
-                       SynonymDeletionConfigurator config) {\r
-               TaxonBase base = this.load(synonymUuid);\r
-               Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);\r
-               base = this.load(taxonUuid);\r
-               Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);\r
\r
-               return this.deleteSynonym(syn, taxon, config);\r
-       }\r
\r
-       @Override\r
-       @Transactional(readOnly = false)\r
-       public UpdateResult swapSynonymAndAcceptedTaxon(UUID synonymUUid,\r
-                       UUID acceptedTaxonUuid) {\r
-               TaxonBase base = this.load(synonymUUid);\r
-               Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);\r
-               base = this.load(acceptedTaxonUuid);\r
-               Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);\r
\r
-               return this.swapSynonymAndAcceptedTaxon(syn, taxon);\r
-       }\r
\r
\r
\r
- }\r
+ // $Id$
+ /**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+ package eu.etaxonomy.cdm.api.service;
+ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.EnumSet;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+ import java.util.UUID;
+ import javax.persistence.EntityNotFoundException;
+ import org.apache.commons.lang.StringUtils;
+ import org.apache.log4j.Logger;
+ import org.apache.lucene.index.CorruptIndexException;
+ import org.apache.lucene.queryparser.classic.ParseException;
+ import org.apache.lucene.search.BooleanClause.Occur;
+ import org.apache.lucene.search.BooleanQuery;
+ import org.apache.lucene.search.BooleanQuery.Builder;
+ import org.apache.lucene.search.Query;
+ import org.apache.lucene.search.SortField;
+ import org.apache.lucene.search.grouping.TopGroups;
+ import org.apache.lucene.search.join.ScoreMode;
+ import org.apache.lucene.util.BytesRef;
+ import org.springframework.beans.factory.annotation.Autowired;
+ import org.springframework.stereotype.Service;
+ import org.springframework.transaction.annotation.Transactional;
+ import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
+ import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
+ import eu.etaxonomy.cdm.api.service.config.IncludedTaxonConfiguration;
+ import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
+ import eu.etaxonomy.cdm.api.service.config.NodeDeletionConfigurator.ChildHandling;
+ import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;
+ import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
+ import eu.etaxonomy.cdm.api.service.dto.FindByIdentifierDTO;
+ import eu.etaxonomy.cdm.api.service.dto.IncludedTaxaDTO;
+ import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
+ import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
+ import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
+ import eu.etaxonomy.cdm.api.service.pager.Pager;
+ import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
+ import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
+ import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
+ import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;
+ import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;
+ import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
+ import eu.etaxonomy.cdm.api.service.search.QueryFactory;
+ import eu.etaxonomy.cdm.api.service.search.SearchResult;
+ import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
+ import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
+ import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
+ import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+ import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
+ import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
+ import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;
+ import eu.etaxonomy.cdm.model.CdmBaseType;
+ import eu.etaxonomy.cdm.model.common.Annotation;
+ import eu.etaxonomy.cdm.model.common.AnnotationType;
+ import eu.etaxonomy.cdm.model.common.CdmBase;
+ import eu.etaxonomy.cdm.model.common.DefinedTerm;
+ import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
+ import eu.etaxonomy.cdm.model.common.IdentifiableSource;
+ import eu.etaxonomy.cdm.model.common.Language;
+ import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
+ import eu.etaxonomy.cdm.model.common.OriginalSourceType;
+ import eu.etaxonomy.cdm.model.common.RelationshipBase;
+ import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
+ import eu.etaxonomy.cdm.model.description.CommonTaxonName;
+ import eu.etaxonomy.cdm.model.description.DescriptionBase;
+ import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+ import eu.etaxonomy.cdm.model.description.Distribution;
+ import eu.etaxonomy.cdm.model.description.Feature;
+ import eu.etaxonomy.cdm.model.description.IIdentificationKey;
+ import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
+ import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+ import eu.etaxonomy.cdm.model.description.SpecimenDescription;
+ import eu.etaxonomy.cdm.model.description.TaxonDescription;
+ import eu.etaxonomy.cdm.model.description.TaxonInteraction;
+ import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
+ import eu.etaxonomy.cdm.model.location.NamedArea;
+ import eu.etaxonomy.cdm.model.media.Media;
+ import eu.etaxonomy.cdm.model.media.MediaRepresentation;
+ import eu.etaxonomy.cdm.model.media.MediaUtils;
+ import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
+ import eu.etaxonomy.cdm.model.name.NameRelationship;
+ import eu.etaxonomy.cdm.model.name.Rank;
+ import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+ import eu.etaxonomy.cdm.model.name.ZoologicalName;
+ import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+ import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+ import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+ import eu.etaxonomy.cdm.model.reference.Reference;
+ import eu.etaxonomy.cdm.model.taxon.Classification;
+ import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
+ import eu.etaxonomy.cdm.model.taxon.Synonym;
+ import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
+ import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
+ import eu.etaxonomy.cdm.model.taxon.Taxon;
+ import eu.etaxonomy.cdm.model.taxon.TaxonBase;
+ import eu.etaxonomy.cdm.model.taxon.TaxonNode;
+ import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
+ import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
+ import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
+ import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
+ import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
+ import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
+ import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
+ import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
+ import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
+ import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
+ import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
+ import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
+ import eu.etaxonomy.cdm.persistence.query.MatchMode;
+ import eu.etaxonomy.cdm.persistence.query.OrderHint;
+ import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
+ import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+ /**
+  * @author a.kohlbecker
+  * @date 10.09.2010
+  *
+  */
+ @Service
+ @Transactional(readOnly = true)
+ public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
+     private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
+     public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";
+     public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";
+     public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";
+     @Autowired
+     private ITaxonNodeDao taxonNodeDao;
+     @Autowired
+     private ITaxonNameDao nameDao;
+     @Autowired
+     private INameService nameService;
+     @Autowired
+     private IOccurrenceService occurrenceService;
+     @Autowired
+     private ITaxonNodeService nodeService;
+     @Autowired
+     private ICdmGenericDao genericDao;
+     @Autowired
+     private IDescriptionService descriptionService;
+     @Autowired
+     private IOrderedTermVocabularyDao orderedVocabularyDao;
+     @Autowired
+     private IOccurrenceDao occurrenceDao;
+     @Autowired
+     private IClassificationDao classificationDao;
+     @Autowired
+     private AbstractBeanInitializer beanInitializer;
+     @Autowired
+     private ILuceneIndexToolProvider luceneIndexToolProvider;
+     /**
+      * Constructor
+      */
+     public TaxonServiceImpl(){
+         if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
+     }
+     /**
+      * FIXME Candidate for harmonization
+      * rename searchByName ?
+      */
+     @Override
+     public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
+         return dao.getTaxaByName(name, sec);
+     }
+     /**
+      * FIXME Candidate for harmonization
+      * merge with getRootTaxa(Reference sec, ..., ...)
+      *  (non-Javadoc)
+      * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
+      */
+     @Override
+     public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
+         if (cdmFetch == null){
+             cdmFetch = CdmFetch.NO_FETCH();
+         }
+         return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
+     }
+     @Override
+     public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
+         return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
+     }
+     @Override
+     public List<RelationshipBase> getAllRelationships(int limit, int start){
+         return dao.getAllRelationships(limit, start);
+     }
+     /**
+      * FIXME Candidate for harmonization
+      * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
+      */
+     @Override
+     @Deprecated
+     public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
+         String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
+         UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
+         OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
+             (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
+         return taxonRelTypeVocabulary;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public UpdateResult swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
+       UpdateResult result = new UpdateResult();
+         TaxonNameBase<?,?> synonymName = synonym.getName();
+         synonymName.removeTaxonBase(synonym);
+         TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
+         taxonName.removeTaxonBase(acceptedTaxon);
+         synonym.setName(taxonName);
+         acceptedTaxon.setName(synonymName);
+         saveOrUpdate(synonym);
+         saveOrUpdate(acceptedTaxon);
+         result.addUpdatedObject(acceptedTaxon);
+         result.addUpdatedObject(synonym);
+               return result;
+         // the accepted taxon needs a new uuid because the concept has changed
+         // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
+         //acceptedTaxon.setUuid(UUID.randomUUID());
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
+         TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
+         TaxonNameBase<?,?> synonymName = synonym.getName();
+         HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
+         //check synonym is not homotypic
+         if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
+             String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
+             throw new HomotypicalGroupChangeException(message);
+         }
+         Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
+         dao.save(newAcceptedTaxon);
+         SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
+         List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
+         Set<NameRelationship> basionymsAndReplacedSynonyms = synonymHomotypicGroup.getBasionymAndReplacedSynonymRelations();
+         for (Synonym heteroSynonym : heteroSynonyms){
+             if (synonym.equals(heteroSynonym)){
+                 acceptedTaxon.removeSynonym(heteroSynonym, false);
+             }else{
+                 //move synonyms in same homotypic group to new accepted taxon
+                 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
+             }
+         }
+         dao.saveOrUpdate(acceptedTaxon);
+         //synonym.getName().removeTaxonBase(synonym);
+         if (deleteSynonym){
+ //                    deleteSynonym(synonym, taxon, false);
+             try {
+                 this.dao.flush();
+                 SynonymDeletionConfigurator config = new SynonymDeletionConfigurator();
+                 config.setDeleteNameIfPossible(false);
+                 this.deleteSynonym(synonym, acceptedTaxon, config);
+             } catch (Exception e) {
+                 logger.info("Can't delete old synonym from database");
+             }
+         }
+         return newAcceptedTaxon;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public UpdateResult changeSynonymToAcceptedTaxon(UUID synonymUuid,
+             UUID acceptedTaxonUuid,
+             UUID newParentNodeUuid,
+             boolean deleteSynonym,
+             boolean copyCitationInfo,
+             Reference citation,
+             String microCitation) throws HomotypicalGroupChangeException {
+         UpdateResult result = new UpdateResult();
+         Synonym synonym = CdmBase.deproxy(dao.load(synonymUuid), Synonym.class);
+         Taxon acceptedTaxon = CdmBase.deproxy(dao.load(acceptedTaxonUuid), Taxon.class);
+         Taxon taxon =  changeSynonymToAcceptedTaxon(synonym, acceptedTaxon, deleteSynonym, copyCitationInfo, citation, microCitation);
+         TaxonNode newParentNode = taxonNodeDao.load(newParentNodeUuid);
+         TaxonNode newNode = newParentNode.addChildTaxon(taxon, null, null);
+         taxonNodeDao.save(newNode);
+         result.addUpdatedObject(taxon);
+         result.addUpdatedObject(acceptedTaxon);
+         result.setCdmEntity(newNode);
+         return result;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public UpdateResult changeSynonymToRelatedTaxon(UUID synonymUuid,
+             UUID toTaxonUuid,
+             TaxonRelationshipType taxonRelationshipType,
+             Reference citation,
+             String microcitation){
+         UpdateResult result = new UpdateResult();
+         Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
+         Synonym synonym = (Synonym) dao.load(synonymUuid);
+         Taxon relatedTaxon = changeSynonymToRelatedTaxon(synonym, toTaxon, taxonRelationshipType, citation, microcitation);
+         result.setCdmEntity(relatedTaxon);
+         result.addUpdatedObject(relatedTaxon);
+         result.addUpdatedObject(toTaxon);
+         return result;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
+         // Get name from synonym
+         TaxonNameBase<?, ?> synonymName = synonym.getName();
+       /*  // remove synonym from taxon
+         toTaxon.removeSynonym(synonym);
+ */
+         // Create a taxon with synonym name
+         Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
+         // Add taxon relation
+         fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
+         // since we are swapping names, we have to detach the name from the synonym completely.
+         // Otherwise the synonym will still be in the list of typified names.
+        // synonym.getName().removeTaxonBase(synonym);
+         this.deleteSynonym(synonym, null);
+         return fromTaxon;
+     }
+     @Transactional(readOnly = false)
+     @Override
+     public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
+                         boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
+         // Get synonym name
+         TaxonNameBase synonymName = synonym.getName();
+         HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
+         // Switch groups
+         oldHomotypicalGroup.removeTypifiedName(synonymName, false);
+         newHomotypicalGroup.addTypifiedName(synonymName);
+         //remove existing basionym relationships
+         synonymName.removeBasionyms();
+         //add basionym relationship
+         if (setBasionymRelationIfApplicable){
+             Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
+             for (TaxonNameBase basionym : basionyms){
+                 synonymName.addBasionym(basionym);
+             }
+         }
+         //set synonym relationship correctly
+ //                    SynonymRelationship relToTaxon = null;
+         boolean relToTargetTaxonExists = false;
+         Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
+         for (SynonymRelationship rel : existingRelations){
+             Taxon acceptedTaxon = rel.getAcceptedTaxon();
+             boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
+             HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
+             boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
+             SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
+             rel.setType(newRelationType);
+             //TODO handle citation and microCitation
+             if (isTargetTaxon){
+                 relToTargetTaxonExists = true;
+             }else{
+                 if (removeFromOtherTaxa){
+                     acceptedTaxon.removeSynonym(synonym, false);
+                 }else{
+                     //do nothing
+                 }
+             }
+         }
+         if (targetTaxon != null &&  ! relToTargetTaxonExists ){
+             Taxon acceptedTaxon = targetTaxon;
+             HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
+             boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
+             SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
+             //TODO handle citation and microCitation
+             Reference citation = null;
+             String microCitation = null;
+             acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
+         }
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
+         if (clazz == null){
+             clazz = TaxonBase.class;
+         }
+         super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+     }
+     @Override
+     @Autowired
+     protected void setDao(ITaxonDao dao) {
+         this.dao = dao;
+     }
+     @Override
+     public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial,        String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
+         Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
+         List<TaxonBase> results = new ArrayList<TaxonBase>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
+         }
+         return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet,     String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
+         Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
+         List<TaxonBase> results = new ArrayList<TaxonBase>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
+         }
+         return results;
+     }
+     @Override
+     public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
+         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
+         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
+         }
+         return results;
+     }
+     @Override
+     public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
+         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
+         }
+         return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
+         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
+         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
+         }
+         return results;
+     }
+     @Override
+     public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
+         List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
+         }
+         return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<Taxon> listAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,
+             List<OrderHint> orderHints, List<String> propertyPaths){
+         return pageAcceptedTaxaFor(synonymUuid, classificationUuid, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
+     }
+     @Override
+     public Pager<Taxon> pageAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,
+             List<OrderHint> orderHints, List<String> propertyPaths){
+         List<Taxon> list = new ArrayList<Taxon>();
+         Long count = 0l;
+         Synonym synonym = null;
+         try {
+             synonym = (Synonym) dao.load(synonymUuid);
+         } catch (ClassCastException e){
+             throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid + " is not a Synonmy");
+         } catch (NullPointerException e){
+             throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid);
+         }
+         Classification classificationFilter = null;
+         if(classificationUuid != null){
+             try {
+             classificationFilter = classificationDao.load(classificationUuid);
+             } catch (NullPointerException e){
+                 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid);
+             }
+             if(classificationFilter == null){
+             }
+         }
+         count = dao.countAcceptedTaxaFor(synonym, classificationFilter) ;
+         if(count > (pageSize * pageNumber)){
+             list = dao.listAcceptedTaxaFor(synonym, classificationFilter, pageSize, pageNumber, orderHints, propertyPaths);
+         }
+         return new DefaultPagerImpl<Taxon>(pageNumber, count.intValue(), pageSize, list);
+     }
+     @Override
+     public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,
+             Integer limit, Integer start, List<String> propertyPaths) {
+         Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<Taxon>(), maxDepth);
+         relatedTaxa.remove(taxon);
+         beanInitializer.initializeAll(relatedTaxa, propertyPaths);
+         return relatedTaxa;
+     }
+     /**
+      * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
+      *  <code>taxon</code> supplied as parameter.
+      *
+      * @param taxon
+      * @param includeRelationships
+      * @param taxa
+      * @param maxDepth can be <code>null</code> for infinite depth
+      * @return
+      */
+     private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa, Integer maxDepth) {
+         if(taxa.isEmpty()) {
+             taxa.add(taxon);
+         }
+         if(includeRelationships.isEmpty()){
+             return taxa;
+         }
+         if(maxDepth != null) {
+             maxDepth--;
+         }
+         if(logger.isDebugEnabled()){
+             logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);
+         }
+         List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null);
+         for (TaxonRelationship taxRel : taxonRelationships) {
+             // skip invalid data
+             if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {
+                 continue;
+             }
+             // filter by includeRelationships
+             for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {
+                 if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) {
+                     if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {
+                         if(logger.isDebugEnabled()){
+                             logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());
+                         }
+                         taxa.add(taxRel.getToTaxon());
+                         if(maxDepth == null || maxDepth > 0) {
+                             taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth));
+                         }
+                     }
+                     if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {
+                         taxa.add(taxRel.getFromTaxon());
+                         if(logger.isDebugEnabled()){
+                             logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );
+                         }
+                         if(maxDepth == null || maxDepth > 0) {
+                             taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth));
+                         }
+                     }
+                 }
+             }
+         }
+         return taxa;
+     }
+     @Override
+     public Pager<SynonymRelationship> getSynonyms(Taxon taxon,        SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         Integer numberOfResults = dao.countSynonyms(taxon, type);
+         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
+         }
+         return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public Pager<SynonymRelationship> getSynonyms(Synonym synonym,    SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+         Integer numberOfResults = dao.countSynonyms(synonym, type);
+         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
+         }
+         return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
+     }
+     @Override
+     public List<List<Synonym>> getSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
+          List<List<Synonym>> result = new ArrayList<List<Synonym>>();
+         Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
+         //homotypic
+         result.add(t.getHomotypicSynonymsByHomotypicGroup());
+         //heterotypic
+         List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
+         for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
+             result.add(t.getSynonymsInGroup(homotypicalGroup));
+         }
+         return result;
+     }
+     @Override
+     public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
+         Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
+         return t.getHomotypicSynonymsByHomotypicGroup();
+     }
+     @Override
+     public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
+         Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
+         List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
+         List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
+         for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
+             heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
+         }
+         return heterotypicSynonymyGroups;
+     }
+     @Override
+     public List<UuidAndTitleCache<IdentifiableEntity>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){
+         List<UuidAndTitleCache<IdentifiableEntity>> results = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>();
+         if (configurator.isDoSynonyms() || configurator.isDoTaxa() || configurator.isDoNamesWithoutTaxa()){
+               results = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.isDoNamesWithoutTaxa(), configurator.isDoMisappliedNames(),configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
+         }
+         if (configurator.isDoTaxaByCommonNames()) {
+             //if(configurator.getPageSize() == null ){
+                 List<UuidAndTitleCache<IdentifiableEntity>> commonNameResults = dao.getTaxaByCommonNameForEditor(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
+                 if(commonNameResults != null){
+                     results.addAll(commonNameResults);
+                 }
+            // }
+         }
+         return results;
+     }
+     @Override
+     public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {
+         List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
+         int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
+         List<TaxonBase> taxa = null;
+         // Taxa and synonyms
+         long numberTaxaResults = 0L;
+         List<String> propertyPath = new ArrayList<String>();
+         if(configurator.getTaxonPropertyPath() != null){
+             propertyPath.addAll(configurator.getTaxonPropertyPath());
+         }
+        if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){
+             if(configurator.getPageSize() != null){ // no point counting if we need all anyway
+                 numberTaxaResults =
+                     dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
+                         configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
+                         configurator.getNamedAreas());
+             }
+             if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
+                 taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
+                     configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),
+                     configurator.getMatchMode(), configurator.getNamedAreas(),
+                     configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
+             }
+        }
+         if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
+         if(taxa != null){
+             results.addAll(taxa);
+         }
+         numberOfResults += numberTaxaResults;
+         // Names without taxa
+         if (configurator.isDoNamesWithoutTaxa()) {
+             int numberNameResults = 0;
+             List<? extends TaxonNameBase<?,?>> names =
+                 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
+                         configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
+             if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
+             if (names.size() > 0) {
+                 for (TaxonNameBase<?,?> taxonName : names) {
+                     if (taxonName.getTaxonBases().size() == 0) {
+                         results.add(taxonName);
+                         numberNameResults++;
+                     }
+                 }
+                 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
+                 numberOfResults += numberNameResults;
+             }
+         }
+         // Taxa from common names
+         if (configurator.isDoTaxaByCommonNames()) {
+             taxa = new ArrayList<TaxonBase>();
+             numberTaxaResults = 0;
+             if(configurator.getPageSize() != null){// no point counting if we need all anyway
+                 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
+             }
+             if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
+                 List<Taxon> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
+                 taxa.addAll(commonNameResults);
+             }
+             if(taxa != null){
+                 results.addAll(taxa);
+             }
+             numberOfResults += numberTaxaResults;
+         }
+        return new DefaultPagerImpl<IdentifiableEntity>
+             (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
+     }
+     public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
+         return dao.getUuidAndTitleCache();
+     }
+     @Override
+     public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
+         List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
+         taxon = (Taxon)dao.load(taxon.getUuid());
+         Set<TaxonDescription> descriptions = taxon.getDescriptions();
+         for (TaxonDescription taxDesc: descriptions){
+             Set<DescriptionElementBase> elements = taxDesc.getElements();
+             for (DescriptionElementBase descElem: elements){
+                 for(Media media : descElem.getMedia()){
+                     //find the best matching representation
+                     medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
+                 }
+             }
+         }
+         return medRep;
+     }
+     @Override
+     public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){
+         return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);
+     }
+     @Override
+     public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
+             Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,
+             Boolean includeTaxonNameDescriptions, List<String> propertyPath) {
+     //    logger.setLevel(Level.TRACE);
+ //        Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
+         logger.trace("listMedia() - START");
+         Set<Taxon> taxa = new HashSet<Taxon>();
+         List<Media> taxonMedia = new ArrayList<Media>();
+         List<Media> nonImageGalleryImages = new ArrayList<Media>();
+         if (limitToGalleries == null) {
+             limitToGalleries = false;
+         }
+         // --- resolve related taxa
+         if (includeRelationships != null && ! includeRelationships.isEmpty()) {
+             logger.trace("listMedia() - resolve related taxa");
+             taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null);
+         }
+         taxa.add((Taxon) dao.load(taxon.getUuid()));
+         if(includeTaxonDescriptions != null && includeTaxonDescriptions){
+             logger.trace("listMedia() - includeTaxonDescriptions");
+             List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();
+             // --- TaxonDescriptions
+             for (Taxon t : taxa) {
+                 taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));
+             }
+             for (TaxonDescription taxonDescription : taxonDescriptions) {
+                 if (!limitToGalleries || taxonDescription.isImageGallery()) {
+                     for (DescriptionElementBase element : taxonDescription.getElements()) {
+                         for (Media media : element.getMedia()) {
+                             if(taxonDescription.isImageGallery()){
+                                 taxonMedia.add(media);
+                             }
+                             else{
+                                 nonImageGalleryImages.add(media);
+                             }
+                         }
+                     }
+                 }
+             }
+             //put images from image gallery first (#3242)
+             taxonMedia.addAll(nonImageGalleryImages);
+         }
+         if(includeOccurrences != null && includeOccurrences) {
+             logger.trace("listMedia() - includeOccurrences");
+             Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<SpecimenOrObservationBase>();
+             // --- Specimens
+             for (Taxon t : taxa) {
+                 specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));
+             }
+             for (SpecimenOrObservationBase occurrence : specimensOrObservations) {
+ //                    direct media removed from specimen #3597
+ //              taxonMedia.addAll(occurrence.getMedia());
+                 // SpecimenDescriptions
+                 Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();
+                 for (DescriptionBase specimenDescription : specimenDescriptions) {
+                     if (!limitToGalleries || specimenDescription.isImageGallery()) {
+                         Set<DescriptionElementBase> elements = specimenDescription.getElements();
+                         for (DescriptionElementBase element : elements) {
+                             for (Media media : element.getMedia()) {
+                                 taxonMedia.add(media);
+                             }
+                         }
+                     }
+                 }
+                 if (occurrence.isInstanceOf(DerivedUnit.class)) {
+                     DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);
+                     // Collection
+                     //TODO why may collections have media attached? #
+                     if (derivedUnit.getCollection() != null){
+                         taxonMedia.addAll(derivedUnit.getCollection().getMedia());
+                     }
+                 }
+                 //media in hierarchy
+                 taxonMedia.addAll(occurrenceService.getMediainHierarchy(occurrence, null, null, propertyPath).getRecords());
+             }
+         }
+         if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {
+             logger.trace("listMedia() - includeTaxonNameDescriptions");
+             // --- TaxonNameDescription
+             Set<TaxonNameDescription> nameDescriptions = new HashSet<TaxonNameDescription>();
+             for (Taxon t : taxa) {
+                 nameDescriptions .addAll(t.getName().getDescriptions());
+             }
+             for(TaxonNameDescription nameDescription: nameDescriptions){
+                 if (!limitToGalleries || nameDescription.isImageGallery()) {
+                     Set<DescriptionElementBase> elements = nameDescription.getElements();
+                     for (DescriptionElementBase element : elements) {
+                         for (Media media : element.getMedia()) {
+                             taxonMedia.add(media);
+                         }
+                     }
+                 }
+             }
+         }
+         logger.trace("listMedia() - initialize");
+         beanInitializer.initializeAll(taxonMedia, propertyPath);
+         logger.trace("listMedia() - END");
+         return taxonMedia;
+     }
+     @Override
+     public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
+         return this.dao.listByIds(listOfIDs, null, null, null, null);
+     }
+     @Override
+     public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
+         return this.dao.findByUuid(uuid, null ,propertyPaths);
+     }
+     @Override
+     public int countAllRelationships() {
+         return this.dao.countAllRelationships();
+     }
+     @Override
+     public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
+         return this.dao.findIdenticalTaxonNames(propertyPath);
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult deleteTaxon(UUID taxonUUID, TaxonDeletionConfigurator config, UUID classificationUuid)  {
+       if (config == null){
+             config = new TaxonDeletionConfigurator();
+         }
+       Taxon taxon = (Taxon)dao.load(taxonUUID);
++      DeleteResult result = new DeleteResult();
++      if (taxon == null){
++          result.setAbort();
++          result.addException(new Exception ("The taxon was already deleted."));
++          return result;
++      }
+       taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);
+       Classification classification = HibernateProxyHelper.deproxy(classificationDao.load(classificationUuid), Classification.class);
 -        DeleteResult result = isDeletable(taxon, config);
++        result = isDeletable(taxon, config);
+         if (result.isOk()){
+             // --- DeleteSynonymRelations
+             if (config.isDeleteSynonymRelations()){
+                 boolean removeSynonymNameFromHomotypicalGroup = false;
+                 // use tmp Set to avoid concurrent modification
+                 Set<SynonymRelationship> synRelsToDelete = new HashSet<SynonymRelationship>();
+                 synRelsToDelete.addAll(taxon.getSynonymRelations());
+                 for (SynonymRelationship synRel : synRelsToDelete){
+                     Synonym synonym = synRel.getSynonym();
+                     // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
+                     // this will cause hibernate to delete the relationship since
+                     // the SynonymRelationship field on both is annotated with removeOrphan
+                     // so no further explicit deleting of the relationship should be done here
+                     taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);
+                     // --- DeleteSynonymsIfPossible
+                     if (config.isDeleteSynonymsIfPossible()){
+                         //TODO which value
+                         boolean newHomotypicGroupIfNeeded = true;
+                         SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();
+                         deleteSynonym(synonym, taxon, synConfig);
+                     }
+                     // relationship will be deleted by hibernate automatically,
+                     // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
+                 }
+             }
+             // --- DeleteTaxonRelationships
+             if (! config.isDeleteTaxonRelationships()){
+                 if (taxon.getTaxonRelations().size() > 0){
+                     result.setAbort();
+                     result.addException(new Exception("Taxon can't be deleted as it is related to another taxon. " +
+                             "Remove taxon from all relations to other taxa prior to deletion."));
+                 }
+             } else{
+                 for (TaxonRelationship taxRel: taxon.getTaxonRelations()){
+                     if (config.isDeleteMisappliedNamesAndInvalidDesignations()){
+                         if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){
+                             if (taxon.equals(taxRel.getToTaxon())){
+                                 this.deleteTaxon(taxRel.getFromTaxon().getUuid(), config, classificationUuid);
+                             }
+                         }
+                     }
+                     taxon.removeTaxonRelation(taxRel);
+                 }
+             }
+             //        TaxonDescription
+             if (config.isDeleteDescriptions()){
+                 Set<TaxonDescription> descriptions = taxon.getDescriptions();
+                 List<TaxonDescription> removeDescriptions = new ArrayList<TaxonDescription>();
+                 for (TaxonDescription desc: descriptions){
+                     //TODO use description delete configurator ?
+                     //FIXME check if description is ALWAYS deletable
+                     if (desc.getDescribedSpecimenOrObservation() != null){
+                         result.setAbort();
+                         result.addException(new Exception("Taxon can't be deleted as it is used in a TaxonDescription" +
+                                 " which also describes specimens or observations"));
+                         break;
+                     }
+                     removeDescriptions.add(desc);
+                 }
+                 if (result.isOk()){
+                     for (TaxonDescription desc: removeDescriptions){
+                         taxon.removeDescription(desc);
+                         descriptionService.delete(desc);
+                     }
+                 } else {
+                     return result;
+                 }
+             }
+          if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null && taxon.getTaxonNodes().size() > 1)){
+                 //if (taxon.getTaxonNodes().size() > 0){
+                  result.addException(new Exception( "Taxon can't be deleted as it is used in more than one classification."));
+                    // throw new ReferencedObjectUndeletableException(message);
+                 //}
+          }else{
+                 if (taxon.getTaxonNodes().size() != 0){
+                     Set<TaxonNode> nodes = taxon.getTaxonNodes();
+                     Iterator<TaxonNode> iterator = nodes.iterator();
+                     TaxonNode node = null;
+                     boolean deleteChildren;
+                     if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
+                         deleteChildren = true;
+                     }else {
+                         deleteChildren = false;
+                     }
+                     boolean success = true;
+                     if (!config.isDeleteInAllClassifications() && !(classification == null)){
+                         while (iterator.hasNext()){
+                             node = iterator.next();
+                             if (node.getClassification().equals(classification)){
+                                 break;
+                             }
+                             node = null;
+                         }
+                         if (node != null){
+                             HibernateProxyHelper.deproxy(node, TaxonNode.class);
+                             success =taxon.removeTaxonNode(node, deleteChildren);
+                             nodeService.delete(node);
+                         } else {
+                               result.setError();
+                               result.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
+                         }
+                     } else if (config.isDeleteInAllClassifications()){
+                         List<TaxonNode> nodesList = new ArrayList<TaxonNode>();
+                         nodesList.addAll(taxon.getTaxonNodes());
+                             for (ITaxonTreeNode treeNode: nodesList){
+                                 TaxonNode taxonNode = (TaxonNode) treeNode;
+                                 if(!deleteChildren){
+                                     Object[] childNodes = taxonNode.getChildNodes().toArray();
+                                     for (Object childNode: childNodes){
+                                         TaxonNode childNodeCast = (TaxonNode) childNode;
+                                         taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());
+                                     }
+                                     //taxon.removeTaxonNode(taxonNode);
+                                 }
+                             }
+                         config.getTaxonNodeConfig().setDeleteElement(false);
+                         DeleteResult resultNodes = nodeService.deleteTaxonNodes(nodesList, config);
+                         if (!resultNodes.isOk()){
+                               result.addExceptions(resultNodes.getExceptions());
+                               result.setStatus(resultNodes.getStatus());
+                         } else {
+                             result.addUpdatedObjects(resultNodes.getUpdatedObjects());
+                         }
+                     }
+                     if (!success){
+                         result.setError();
+                         result.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
+                     }
+                 }
+             }
+             //TaxonNameBase
+             if (config.isDeleteNameIfPossible() && result.isOk()){
+                     //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
+                     TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName());
+                     //check whether taxon will be deleted or not
+                     if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && name != null ){
+                         //name.removeTaxonBase(taxon);
+                         //nameService.saveOrUpdate(name);
+                         taxon.setName(null);
+                         //dao.delete(taxon);
+                         DeleteResult nameResult = new DeleteResult();
+                         //remove name if possible (and required)
+                         if (name != null && config.isDeleteNameIfPossible()){
+                               nameResult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());
+                         }
+                         if (nameResult.isError() || nameResult.isAbort()){
+                               //result.setError();
+                               result.addRelatedObject(name);
+                               result.addExceptions(nameResult.getExceptions());
+                         }
+                     }
+             }else {
+                 taxon.setName(null);
+             }
+             if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0)  && result.isOk()){
+               try{
+                       UUID uuid = dao.delete(taxon);
+               }catch(Exception e){
+                       result.addException(e);
+                       result.setError();
+               }
+             } else {
+               result.setError();
+               result.addException(new Exception("The Taxon can't be deleted because it is used in a classification."));
+             }
+         }
+         return result;
+     }
+     private String checkForReferences(Taxon taxon){
+         Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);
+         for (CdmBase referencingObject : referencingObjects){
+             //IIdentificationKeys (Media, Polytomous, MultiAccess)
+             if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){
+                 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";
+                 return message;
+             }
+            /* //PolytomousKeyNode
+             if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
+                 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
+                 return message;
+             }*/
+             //TaxonInteraction
+             if (referencingObject.isInstanceOf(TaxonInteraction.class)){
+                 String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
+                 return message;
+             }
+           //TaxonInteraction
+             if (referencingObject.isInstanceOf(DeterminationEvent.class)){
+                 String message = "Taxon can't be deleted as it is used in a determination event";
+                 return message;
+             }
+         }
+         referencingObjects = null;
+         return null;
+     }
+     private boolean checkForPolytomousKeys(Taxon taxon){
+         boolean result = false;
+         List<CdmBase> list = genericDao.getCdmBasesByFieldAndClass(PolytomousKeyNode.class, "taxon", taxon);
+         if (!list.isEmpty()) {
+             result = true;
+         }
+         return result;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult delete(UUID synUUID){
+       DeleteResult result = new DeleteResult();
+       Synonym syn = (Synonym)dao.load(synUUID);
+         return this.deleteSynonym(syn, null);
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {
+         return deleteSynonym(synonym, null, config);
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public DeleteResult deleteSynonym(UUID synonymUuid, SynonymDeletionConfigurator config) {
+         return deleteSynonym((Synonym)dao.load(synonymUuid), config);
+     }
+     @Transactional(readOnly = false)
+     @Override
+     public DeleteResult deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) {
+         DeleteResult result = new DeleteResult();
+       if (synonym == null){
+               result.setAbort();
++              result.addException(new Exception("The synonym was already deleted."));
+               return result;
+         }
+         if (config == null){
+             config = new SynonymDeletionConfigurator();
+         }
++
+         result = isDeletable(synonym, config);
+         if (result.isOk()){
+             synonym = HibernateProxyHelper.deproxy(this.load(synonym.getUuid()), Synonym.class);
+             //remove synonymRelationship
+             Set<Taxon> taxonSet = new HashSet<Taxon>();
+             if (taxon != null){
+                 taxonSet.add(taxon);
+             }else{
+                 taxonSet.addAll(synonym.getAcceptedTaxa());
+             }
+             for (Taxon relatedTaxon : taxonSet){
+               relatedTaxon = HibernateProxyHelper.deproxy(relatedTaxon, Taxon.class);
+                 relatedTaxon.removeSynonym(synonym, false);
+                 this.saveOrUpdate(relatedTaxon);
+             }
+             this.saveOrUpdate(synonym);
+             //TODO remove name from homotypical group?
+             //remove synonym (if necessary)
+             result.addUpdatedObject(taxon);
+             if (synonym.getSynonymRelations().isEmpty()){
+                 TaxonNameBase<?,?> name = synonym.getName();
+                 synonym.setName(null);
+                 dao.delete(synonym);
+                 //remove name if possible (and required)
+                 if (name != null && config.isDeleteNameIfPossible()){
+                         DeleteResult nameDeleteresult = nameService.delete(name.getUuid(), config.getNameDeletionConfig());
+                         if (nameDeleteresult.isAbort()){
+                               result.addExceptions(nameDeleteresult.getExceptions());
+                               result.addRelatedObject(name);
+                         }
+                 }
+             }else {
+               result.setError();
+               result.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
+                 return result;
+             }
+         }
+         return result;
+ //        else{
+ //            List<Exception> exceptions = new ArrayList<Exception>();
+ //            for (String message :messages){
+ //                    exceptions.add(new ReferencedObjectUndeletableException(message));
+ //            }
+ //            result.setError();
+ //            result.addExceptions(exceptions);
+ //            return result;
+ //        }
+     }
+     @Override
+     public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
+         return this.dao.findIdenticalNamesNew(propertyPath);
+     }
+     @Override
+     public String getPhylumName(TaxonNameBase name){
+         return this.dao.getPhylumName(name);
+     }
+     @Override
+     public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {
+         return dao.deleteSynonymRelationships(syn, taxon);
+     }
+     @Override
+     public long deleteSynonymRelationships(Synonym syn) {
+         return dao.deleteSynonymRelationships(syn, null);
+     }
+     @Override
+     public List<SynonymRelationship> listSynonymRelationships(
+             TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
+             List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
+         Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
+         List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
+         if(numberOfResults > 0) { // no point checking again
+             results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
+         }
+         return results;
+     }
+     @Override
+     public Taxon findBestMatchingTaxon(String taxonName) {
+         MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
+         config.setTaxonNameTitle(taxonName);
+         return findBestMatchingTaxon(config);
+     }
+     @Override
+     public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
+         Taxon bestCandidate = null;
+         try{
+             // 1. search for acceptet taxa
+             List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
+             boolean bestCandidateMatchesSecUuid = false;
+             boolean bestCandidateIsInClassification = false;
+             int countEqualCandidates = 0;
+             for(TaxonBase taxonBaseCandidate : taxonList){
+                 if(taxonBaseCandidate instanceof Taxon){
+                     Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
+                     boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
+                     if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
+                         continue;
+                     }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
+                         bestCandidate = newCanditate;
+                         countEqualCandidates = 1;
+                         bestCandidateMatchesSecUuid = true;
+                         continue;
+                     }
+                     boolean newCandidateInClassification = isInClassification(newCanditate, config);
+                     if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
+                         continue;
+                     }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
+                         bestCandidate = newCanditate;
+                         countEqualCandidates = 1;
+                         bestCandidateIsInClassification = true;
+                         continue;
+                     }
+                     if (bestCandidate == null){
+                         bestCandidate = newCanditate;
+                         countEqualCandidates = 1;
+                         continue;
+                     }
+                 }else{  //not Taxon.class
+                     continue;
+                 }
+                 countEqualCandidates++;
+             }
+             if (bestCandidate != null){
+                 if(countEqualCandidates > 1){
+                     logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
+                     return bestCandidate;
+                 } else {
+                     logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
+                     return bestCandidate;
+                 }
+             }
+             // 2. search for synonyms
+             if (config.isIncludeSynonyms()){
+                 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
+                 for(TaxonBase taxonBase : synonymList){
+                     if(taxonBase instanceof Synonym){
+                         Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
+                         Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
+                         if(!acceptetdCandidates.isEmpty()){
+                             bestCandidate = acceptetdCandidates.iterator().next();
+                             if(acceptetdCandidates.size() == 1){
+                                 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
+                                 return bestCandidate;
+                             } else {
+                                 logger.info("using accepted Taxon " +  bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
+                                 return bestCandidate;
+                             }
+                             //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
+                         }
+                     }
+                 }
+             }
+         } catch (Exception e){
+             logger.error(e);
+             e.printStackTrace();
+         }
+         return bestCandidate;
+     }
+     private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
+         UUID configClassificationUuid = config.getClassificationUuid();
+         if (configClassificationUuid == null){
+             return false;
+         }
+         for (TaxonNode node : taxon.getTaxonNodes()){
+             UUID classUuid = node.getClassification().getUuid();
+             if (configClassificationUuid.equals(classUuid)){
+                 return true;
+             }
+         }
+         return false;
+     }
+     private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
+         UUID configSecUuid = config.getSecUuid();
+         if (configSecUuid == null){
+             return false;
+         }
+         UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
+         return configSecUuid.equals(taxonSecUuid);
+     }
+     @Override
+     public Synonym findBestMatchingSynonym(String taxonName) {
+         List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
+         if(! synonymList.isEmpty()){
+             Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
+             if(synonymList.size() == 1){
+                 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
+                 return result;
+             } else {
+                 logger.info("Several matching synonyms found. Using first: " +  result.getTitleCache());
+                 return result;
+             }
+         }
+         return null;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation,
+             Taxon newTaxon,
+             boolean moveHomotypicGroup,
+             SynonymRelationshipType newSynonymRelationshipType,
+             Reference reference,
+             String referenceDetail,
+             boolean keepReference) throws HomotypicalGroupChangeException {
+         Synonym synonym = (Synonym) dao.load(oldSynonymRelation.getSynonym().getUuid());
+         Taxon fromTaxon = (Taxon) dao.load(oldSynonymRelation.getAcceptedTaxon().getUuid());
+         //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
+         TaxonNameBase<?,?> synonymName = synonym.getName();
+         TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
+         //set default relationship type
+         if (newSynonymRelationshipType == null){
+             newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
+         }
+         boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
+         HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
+         int hgSize = homotypicGroup.getTypifiedNames().size();
+         boolean isSingleInGroup = !(hgSize > 1);
+         if (! isSingleInGroup){
+             boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
+             boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
+             if (isHomotypicToAccepted){
+                 String message = "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";
+                 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
+                 message = String.format(message, homotypicRelatives);
+                 throw new HomotypicalGroupChangeException(message);
+             }
+             if (! moveHomotypicGroup){
+                 String message = "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";
+                 throw new HomotypicalGroupChangeException(message);
+             }
+         }else{
+             moveHomotypicGroup = true;  //single synonym always allows to moveCompleteGroup
+         }
+ //        Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
+         SynonymRelationship result = null;
+         //move all synonyms to new taxon
+         List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
+         for (Synonym syn: homotypicSynonyms){
+             Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
+             for (SynonymRelationship synRelation : synRelations){
+                 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
+                     Reference<?> newReference = reference;
+                     if (newReference == null && keepReference){
+                         newReference = synRelation.getCitation();
+                     }
+                     String newRefDetail = referenceDetail;
+                     if (newRefDetail == null && keepReference){
+                         newRefDetail = synRelation.getCitationMicroReference();
+                     }
+                     newTaxon = HibernateProxyHelper.deproxy(newTaxon, Taxon.class);
+                     fromTaxon = HibernateProxyHelper.deproxy(fromTaxon, Taxon.class);
+                     SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
+                     fromTaxon.removeSynonymRelation(synRelation, false);
+ //
+                     //change homotypic group of synonym if relType is 'homotypic'
+ //                    if (newRelTypeIsHomotypic){
+ //                            newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
+ //                    }
+                     //set result
+                     if (synRelation.equals(oldSynonymRelation)){
+                         result = newSynRelation;
+                     }
+                 }
+             }
+         }
+         saveOrUpdate(fromTaxon);
+         saveOrUpdate(newTaxon);
+         //Assert that there is a result
+         if (result == null){
+             String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
+             throw new IllegalStateException(message);
+         }
+         return result;
+     }
+     @Override
+     public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
+         return dao.getUuidAndTitleCacheTaxon();
+     }
+     @Override
+     public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
+         return dao.getUuidAndTitleCacheSynonym();
+     }
+     @Override
+     public Pager<SearchResult<TaxonBase>> findByFullText(
+             Class<? extends TaxonBase> clazz, String queryString,
+             Classification classification, List<Language> languages,
+             boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
+         LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments, null);
+         // --- execute search
+         TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         idFieldMap.put(CdmBaseType.TAXON, "id");
+         // ---  initialize taxa, thighlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
+         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
+     }
+     @Override
+     public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTerm> statusFilter,
+             Classification classification,
+             Integer pageSize, Integer pageNumber,
+             List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, ParseException {
+         LuceneSearch luceneSearch = prepareByDistributionSearch(areaFilter, statusFilter, classification);
+         // --- execute search
+         TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         idFieldMap.put(CdmBaseType.TAXON, "id");
+         // ---  initialize taxa, thighlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
+         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
+     }
+     /**
+      * @param clazz
+      * @param queryString
+      * @param classification
+      * @param languages
+      * @param highlightFragments
+      * @param sortFields TODO
+      * @param directorySelectClass
+      * @return
+      */
+     protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,
+             boolean highlightFragments, SortField[] sortFields) {
+         Builder finalQueryBuilder = new Builder();
+         Builder textQueryBuilder = new Builder();
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
+         QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
+         if(sortFields == null){
+             sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING,  false)};
+         }
+         luceneSearch.setSortFields(sortFields);
+         // ---- search criteria
+         luceneSearch.setCdmTypRestriction(clazz);
+         if(!queryString.isEmpty() && !queryString.equals("*") && !queryString.equals("?") ) {
+             textQueryBuilder.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
+             textQueryBuilder.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);
+         }
+         BooleanQuery textQuery = textQueryBuilder.build();
+         if(textQuery.clauses().size() > 0) {
+             finalQueryBuilder.add(textQuery, Occur.MUST);
+         }
+         if(classification != null){
+             finalQueryBuilder.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
+         }
+         luceneSearch.setQuery(finalQueryBuilder.build());
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     /**
+      * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
+      * the BlockJoinQuery could be used. The latter might be more memory save but has the
+      * drawback of requiring to do the join an indexing time.
+      * see  http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
+      *
+      * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
+      * <ul>
+      * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --&gt; Taxon.id </li>
+      * <li>inverse: {@link Direction.relatedFrom}:  TaxonRelationShip.relatedFrom.id --&gt; Taxon.id </li>
+      * <ul>
+      * @param queryString
+      * @param classification
+      * @param languages
+      * @param highlightFragments
+      * @param sortFields TODO
+      *
+      * @return
+      * @throws IOException
+      */
+     protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List<Language> languages,
+             boolean highlightFragments, SortField[] sortFields) throws IOException {
+         String fromField;
+         String queryTermField;
+         String toField = "id"; // TaxonBase.uuid
+         if(edge.isBidirectional()){
+             throw new RuntimeException("Bidirectional joining not supported!");
+         }
+         if(edge.isEvers()){
+             fromField = "relatedFrom.id";
+             queryTermField = "relatedFrom.titleCache";
+         } else if(edge.isInvers()) {
+             fromField = "relatedTo.id";
+             queryTermField = "relatedTo.titleCache";
+         } else {
+             throw new RuntimeException("Invalid direction: " + edge.getDirections());
+         }
+         Builder finalQueryBuilder = new Builder();
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
+         QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
+         Builder joinFromQueryBuilder = new Builder();
+         joinFromQueryBuilder.add(taxonBaseQueryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);
+         joinFromQueryBuilder.add(taxonBaseQueryFactory.newEntityIdQuery("type.id", edge.getTaxonRelationshipType()), Occur.MUST);
+         Query joinQuery = taxonBaseQueryFactory.newJoinQuery(TaxonRelationship.class, fromField, false, joinFromQueryBuilder.build(), toField, null, ScoreMode.Max);
+         if(sortFields == null){
+             sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING,  false)};
+         }
+         luceneSearch.setSortFields(sortFields);
+         finalQueryBuilder.add(joinQuery, Occur.MUST);
+         if(classification != null){
+             finalQueryBuilder.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
+         }
+         luceneSearch.setQuery(finalQueryBuilder.build());
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     @Override
+     public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(
+             EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,
+             Set<NamedArea> namedAreas, Set<PresenceAbsenceTerm> distributionStatus, List<Language> languages,
+             boolean highlightFragments, Integer pageSize,
+             Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)
+             throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {
+         // FIXME: allow taxonomic ordering
+         //  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";
+         // this require building a special sort column by a special classBridge
+         if(highlightFragments){
+             logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
+                     "currently not fully supported by this method and thus " +
+                     "may not work with common names and misapplied names.");
+         }
+         // convert sets to lists
+         List<NamedArea> namedAreaList = null;
+         List<PresenceAbsenceTerm>distributionStatusList = null;
+         if(namedAreas != null){
+             namedAreaList = new ArrayList<NamedArea>(namedAreas.size());
+             namedAreaList.addAll(namedAreas);
+         }
+         if(distributionStatus != null){
+             distributionStatusList = new ArrayList<PresenceAbsenceTerm>(distributionStatus.size());
+             distributionStatusList.addAll(distributionStatus);
+         }
+         // set default if parameter is null
+         if(searchModes == null){
+             searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);
+         }
+         // set sort order and thus override any sort orders which may have been
+         // defined by prepare*Search methods
+         if(orderHints == null){
+             orderHints = OrderHint.NOMENCLATURAL_SORT_ORDER;
+         }
+         SortField[] sortFields = new SortField[orderHints.size()];
+         int i = 0;
+         for(OrderHint oh : orderHints){
+             sortFields[i++] = oh.toSortField();
+         }
+ //        SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
+ //        SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
+         boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;
+         List<LuceneSearch> luceneSearches = new ArrayList<LuceneSearch>();
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         /*
+           ======== filtering by distribution , HOWTO ========
+            - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
+            - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
+           add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
+           which will be put into a FilteredQuersy  in the end ?
+           3. how does it work in spatial?
+           see
+            - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
+            - http://www.infoq.com/articles/LuceneSpatialSupport
+            - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
+           ------------------------------------------------------------------------
+           filter strategies:
+           A) use a separate distribution filter per index sub-query/search:
+            - byTaxonSyonym (query TaxaonBase):
+                use a join area filter (Distribution -> TaxonBase)
+            - byCommonName (query DescriptionElementBase): use an area filter on
+                DescriptionElementBase !!! PROBLEM !!!
+                This cannot work since the distributions are different entities than the
+                common names and thus these are different lucene documents.
+            - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
+                use a join area filter (Distribution -> TaxonBase)
+           B) use a common distribution filter for all index sub-query/searches:
+            - use a common join area filter (Distribution -> TaxonBase)
+            - also implement the byCommonName as join query (CommonName -> TaxonBase)
+            PROBLEM in this case: we are losing the fragment highlighting for the
+            common names, since the returned documents are always TaxonBases
+         */
+         /* The QueryFactory for creating filter queries on Distributions should
+          * The query factory used for the common names query cannot be reused
+          * for this case, since we want to only record the text fields which are
+          * actually used in the primary query
+          */
+         QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);
+         Builder multiIndexByAreaFilterBuilder = new Builder();
+         // search for taxa or synonyms
+         if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
+             Class taxonBaseSubclass = TaxonBase.class;
+             if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
+                 taxonBaseSubclass = Taxon.class;
+             } else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
+                 taxonBaseSubclass = Synonym.class;
+             }
+             luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments, sortFields));
+             idFieldMap.put(CdmBaseType.TAXON, "id");
+             /* A) does not work!!!!
+             if(addDistributionFilter){
+                 // in this case we need a filter which uses a join query
+                 // to get the TaxonBase documents for the DescriptionElementBase documents
+                 // which are matching the areas in question
+                 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
+                         namedAreaList,
+                         distributionStatusList,
+                         distributionFilterQueryFactory
+                         );
+                 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
+             }
+             */
+             if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
+                 // add additional area filter for synonyms
+                 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
+                 String toField = "accTaxon.id"; // id in TaxonBase index (is multivalued)
+                 //TODO replace by createByDistributionJoinQuery
+                 BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
+                 Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(Distribution.class, fromField, true, byDistributionQuery, toField, Taxon.class, ScoreMode.None);
+                 multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
+             }
+         }
+         // search by CommonTaxonName
+         if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {
+             // B)
+             QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
+             Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(
+                     CommonTaxonName.class,
+                     "inDescription.taxon.id",
+                     true,
+                     QueryFactory.addTypeRestriction(
+                                 createByDescriptionElementFullTextQuery(queryString, classification, null, languages, descriptionElementQueryFactory)
+                                 , CommonTaxonName.class
+                                 ).build(), "id", null, ScoreMode.Max);
+             logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());
+             LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
+             byCommonNameSearch.setCdmTypRestriction(Taxon.class);
+             byCommonNameSearch.setQuery(byCommonNameJoinQuery);
+             byCommonNameSearch.setSortFields(sortFields);
+             idFieldMap.put(CdmBaseType.TAXON, "id");
+             luceneSearches.add(byCommonNameSearch);
+             /* A) does not work!!!!
+             luceneSearches.add(
+                     prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
+                             queryString, classification, null, languages, highlightFragments)
+                         );
+             idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
+             if(addDistributionFilter){
+                 // in this case we are able to use DescriptionElementBase documents
+                 // which are matching the areas in question directly
+                 BooleanQuery byDistributionQuery = createByDistributionQuery(
+                         namedAreaList,
+                         distributionStatusList,
+                         distributionFilterQueryFactory
+                         );
+                 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
+             } */
+         }
+         // search by misapplied names
+         if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)) {
+             // NOTE:
+             // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
+             // which allows doing query time joins
+             // finds the misapplied name (Taxon B) which is an misapplication for
+             // a related Taxon A.
+             //
+             luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
+                     new TaxonRelationshipEdge(TaxonRelationshipType.MISAPPLIED_NAME_FOR(), Direction.relatedTo),
+                     queryString, classification, languages, highlightFragments, sortFields));
+             idFieldMap.put(CdmBaseType.TAXON, "id");
+             if(addDistributionFilter){
+                 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
+                 /*
+                  * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
+                  * Maybe this is a but in java itself java.
+                  *
+                  * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
+                  * directly:
+                  *
+                  *    String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
+                  *
+                  * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
+                  * will execute as expected:
+                  *
+                  *    String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
+                  *    String toField = "relation." + misappliedNameForUuid +".to.id";
+                  *
+                  * Comparing both strings by the String.equals method returns true, so both String are identical.
+                  *
+                  * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
+                  * 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)
+                  * The bug is persistent after a reboot of the development computer.
+                  */
+ //                String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
+ //                String toField = "relation." + misappliedNameForUuid +".to.id";
+                 String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
+ //                System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
+ //                System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
+                 //TODO replace by createByDistributionJoinQuery
+                 BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
+                 Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(Distribution.class, fromField, true, byDistributionQuery, toField, null, ScoreMode.None);
+ //                debug code for bug described above
+                 //does not compile anymore since changing from lucene 3.6.2 to lucene 4.10+
+ //                DocIdSet filterMatchSet = filter.getDocIdSet(luceneIndexToolProvider.getIndexReaderFor(Taxon.class));
+ //                System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
+                 multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
+             }
+         }
+         LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,
+                 luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));
+         if(addDistributionFilter){
+             // B)
+             // in this case we need a filter which uses a join query
+             // to get the TaxonBase documents for the DescriptionElementBase documents
+             // which are matching the areas in question
+             //
+             // for toTaxa, doByCommonName
+             Query taxonAreaJoinQuery = createByDistributionJoinQuery(
+                     namedAreaList,
+                     distributionStatusList,
+                     distributionFilterQueryFactory,
+                     null, true
+                     );
+             multiIndexByAreaFilterBuilder.add(taxonAreaJoinQuery, Occur.SHOULD);
+         }
+         if (addDistributionFilter){
+             multiSearch.setFilter(multiIndexByAreaFilterBuilder.build());
+         }
+         // --- execute search
+         TopGroups<BytesRef> topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
+         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
+         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
+     }
+     /**
+      * @param namedAreaList at least one area must be in the list
+      * @param distributionStatusList optional
+      * @param toType toType
+      *      Optional parameter. Only used for debugging to print the toType documents
+      * @param asFilter TODO
+      * @return
+      * @throws IOException
+      */
+     protected Query createByDistributionJoinQuery(
+             List<NamedArea> namedAreaList,
+             List<PresenceAbsenceTerm> distributionStatusList,
+             QueryFactory queryFactory, Class<? extends CdmBase> toType, boolean asFilter
+             ) throws IOException {
+         String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
+         String toField = "id"; // id in toType usually this is the TaxonBase index
+         BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);
+         ScoreMode scoreMode = asFilter ?  ScoreMode.None : ScoreMode.Max;
+         Query taxonAreaJoinQuery = queryFactory.newJoinQuery(Distribution.class, fromField, false, byDistributionQuery, toField, toType, scoreMode);
+         return taxonAreaJoinQuery;
+     }
+     /**
+      * @param namedAreaList
+      * @param distributionStatusList
+      * @param queryFactory
+      * @return
+      */
+     private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,
+             List<PresenceAbsenceTerm> distributionStatusList, QueryFactory queryFactory) {
+         Builder areaQueryBuilder = new Builder();
+         // area field from Distribution
+         areaQueryBuilder.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);
+         // status field from Distribution
+         if(distributionStatusList != null && distributionStatusList.size() > 0){
+             areaQueryBuilder.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);
+         }
+         BooleanQuery areaQuery = areaQueryBuilder.build();
+         logger.debug("createByDistributionQuery() query: " + areaQuery.toString());
+         return areaQuery;
+     }
+     /**
+      * This method has been primarily created for testing the area join query but might
+      * also be useful in other situations
+      *
+      * @param namedAreaList
+      * @param distributionStatusList
+      * @param classification
+      * @param highlightFragments
+      * @return
+      * @throws IOException
+      */
+     protected LuceneSearch prepareByDistributionSearch(
+             List<NamedArea> namedAreaList, List<PresenceAbsenceTerm> distributionStatusList,
+             Classification classification) throws IOException {
+         Builder finalQueryBuilder = new Builder();
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
+         // FIXME is this query factory using the wrong type?
+         QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);
+         SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.Type.STRING, false)};
+         luceneSearch.setSortFields(sortFields);
+         Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory, null, false);
+         finalQueryBuilder.add(byAreaQuery, Occur.MUST);
+         if(classification != null){
+             finalQueryBuilder.add(taxonQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
+         }
+         BooleanQuery finalQuery = finalQueryBuilder.build();
+         logger.info("prepareByAreaSearch() query: " + finalQuery.toString());
+         luceneSearch.setQuery(finalQuery);
+         return luceneSearch;
+     }
+     @Override
+     public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
+             Class<? extends DescriptionElementBase> clazz, String queryString,
+             Classification classification, List<Feature> features, List<Language> languages,
+             boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
+         LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);
+         // --- execute search
+         TopGroups<BytesRef> topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
+         @SuppressWarnings("rawtypes")
+         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
+         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
+     }
+     @Override
+     public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,
+             Classification classification, List<Language> languages, boolean highlightFragments,
+             Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {
+         LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);
+         LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null);
+         LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);
+         // --- execute search
+         TopGroups<BytesRef> topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
+         // --- initialize taxa, highlight matches ....
+         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
+         Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
+         idFieldMap.put(CdmBaseType.TAXON, "id");
+         idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
+         List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
+                 topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
+         int totalHits = topDocsResultSet != null ? topDocsResultSet.totalGroupCount : 0;
+         return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
+     }
+     /**
+      * @param clazz
+      * @param queryString
+      * @param classification
+      * @param features
+      * @param languages
+      * @param highlightFragments
+      * @param directorySelectClass
+      * @return
+      */
+     protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,
+             String queryString, Classification classification, List<Feature> features,
+             List<Language> languages, boolean highlightFragments) {
+         LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);
+         QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
+         SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.Type.STRING, false)};
+         BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, features,
+                 languages, descriptionElementQueryFactory);
+         luceneSearch.setSortFields(sortFields);
+         luceneSearch.setCdmTypRestriction(clazz);
+         luceneSearch.setQuery(finalQuery);
+         if(highlightFragments){
+             luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());
+         }
+         return luceneSearch;
+     }
+     /**
+      * @param queryString
+      * @param classification
+      * @param features
+      * @param languages
+      * @param descriptionElementQueryFactory
+      * @return
+      */
+     private BooleanQuery createByDescriptionElementFullTextQuery(String queryString, Classification classification,
+             List<Feature> features, List<Language> languages, QueryFactory descriptionElementQueryFactory) {
+         Builder finalQueryBuilder = new Builder();
+         Builder textQueryBuilder = new Builder();
+         textQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
+         // common name
+         Builder nameQueryBuilder = new Builder();
+         if(languages == null || languages.size() == 0){
+             nameQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);
+         } else {
+             Builder languageSubQueryBuilder = new Builder();
+             for(Language lang : languages){
+                 languageSubQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("language.uuid",  lang.getUuid().toString(), false), Occur.SHOULD);
+             }
+             nameQueryBuilder.add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);
+             nameQueryBuilder.add(languageSubQueryBuilder.build(), Occur.MUST);
+         }
+         textQueryBuilder.add(nameQueryBuilder.build(), Occur.SHOULD);
+         // text field from TextData
+         textQueryBuilder.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);
+         // --- TermBase fields - by representation ----
+         // state field from CategoricalData
+         textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);
+         // state field from CategoricalData
+         textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);
+         // area field from Distribution
+         textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);
+         // status field from Distribution
+         textQueryBuilder.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);
+         finalQueryBuilder.add(textQueryBuilder.build(), Occur.MUST);
+         // --- classification ----
+         if(classification != null){
+             finalQueryBuilder.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
+         }
+         // --- IdentifieableEntity fields - by uuid
+         if(features != null && features.size() > 0 ){
+             finalQueryBuilder.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);
+         }
+         // the description must be associated with a taxon
+         finalQueryBuilder.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);
+         BooleanQuery finalQuery = finalQueryBuilder.build();
+         logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());
+         return finalQuery;
+     }
+     /**
+      * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
+      * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
+      * This method is a convenient means to retrieve a Lucene query string for such the fields.
+      *
+      * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
+      * or {@link MultilanguageTextFieldBridge }
+      * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
+      * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
+      * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
+      *
+      * TODO move to utiliy class !!!!!!!!
+      */
+     private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
+         if(stringBuilder == null){
+             stringBuilder = new StringBuilder();
+         }
+         if(languages == null || languages.size() == 0){
+             stringBuilder.append(name + ".ALL:(%1$s) ");
+         } else {
+             for(Language lang : languages){
+                 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
+             }
+         }
+         return stringBuilder;
+     }
+     @Override
+     public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
+         List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
+         List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
+         HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
+         UUID nameUuid= taxon.getName().getUuid();
+         ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);
+         String epithetOfTaxon = null;
+         String infragenericEpithetOfTaxon = null;
+         String infraspecificEpithetOfTaxon = null;
+         if (taxonName.isSpecies()){
+              epithetOfTaxon= taxonName.getSpecificEpithet();
+         } else if (taxonName.isInfraGeneric()){
+             infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
+         } else if (taxonName.isInfraSpecific()){
+             infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
+         }
+         String genusOfTaxon = taxonName.getGenusOrUninomial();
+         Set<TaxonNode> nodes = taxon.getTaxonNodes();
+         List<String> taxonNames = new ArrayList<String>();
+         for (TaxonNode node: nodes){
+            // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
+            // List<String> synonymsEpithet = new ArrayList<String>();
+             if (node.getClassification().equals(classification)){
+                 if (!node.isTopmostNode()){
+                     TaxonNode parent = node.getParent();
+                     parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
+                     TaxonNameBase<?,?> parentName =  parent.getTaxon().getName();
+                     ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
+                     Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
+                     Rank rankOfTaxon = taxonName.getRank();
+                     //create inferred synonyms for species, subspecies
+                     if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
+                         Synonym inferredEpithet = null;
+                         Synonym inferredGenus = null;
+                         Synonym potentialCombination = null;
+                         List<String> propertyPaths = new ArrayList<String>();
+                         propertyPaths.add("synonym");
+                         propertyPaths.add("synonym.name");
+                         List<OrderHint> orderHints = new ArrayList<OrderHint>();
+                         orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
+                         List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
+                         List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
+                         List<TaxonRelationship> taxonRelListParent = null;
+                         List<TaxonRelationship> taxonRelListTaxon = null;
+                         if (doWithMisappliedNames){
+                             taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
+                             taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
+                         }
+                         if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
+                             for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
+                                 Synonym syn = synonymRelationOfParent.getSynonym();
+                                 inferredEpithet = createInferredEpithets(taxon,
+                                         zooHashMap, taxonName, epithetOfTaxon,
+                                         infragenericEpithetOfTaxon,
+                                         infraspecificEpithetOfTaxon,
+                                         taxonNames, parentName,
+                                         syn);
+                                 inferredSynonyms.add(inferredEpithet);
+                                 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
+                                 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
+                             }
+                             if (doWithMisappliedNames){
+                                 for (TaxonRelationship taxonRelationship: taxonRelListParent){
+                                      Taxon misappliedName = taxonRelationship.getFromTaxon();
+                                      inferredEpithet = createInferredEpithets(taxon,
+                                              zooHashMap, taxonName, epithetOfTaxon,
+                                              infragenericEpithetOfTaxon,
+                                              infraspecificEpithetOfTaxon,
+                                              taxonNames, parentName,
+                                              misappliedName);
+                                     inferredSynonyms.add(inferredEpithet);
+                                     zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
+                                      taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
+                                 }
+                             }
+                             if (!taxonNames.isEmpty()){
+                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                             ZoologicalName name;
+                             if (!synNotInCDM.isEmpty()){
+                                 inferredSynonymsToBeRemoved.clear();
+                                 for (Synonym syn :inferredSynonyms){
+                                     name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                     if (!synNotInCDM.contains(name.getNameCache())){
+                                         inferredSynonymsToBeRemoved.add(syn);
+                                     }
+                                 }
+                                 // Remove identified Synonyms from inferredSynonyms
+                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                     inferredSynonyms.remove(synonym);
+                                 }
+                             }
+                         }
+                     }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
+                         for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
+                             TaxonNameBase synName;
+                             ZoologicalName inferredSynName;
+                             Synonym syn = synonymRelationOfTaxon.getSynonym();
+                             inferredGenus = createInferredGenus(taxon,
+                                     zooHashMap, taxonName, epithetOfTaxon,
+                                     genusOfTaxon, taxonNames, zooParentName, syn);
+                             inferredSynonyms.add(inferredGenus);
+                             zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
+                             taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
+                         }
+                         if (doWithMisappliedNames){
+                             for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
+                                 Taxon misappliedName = taxonRelationship.getFromTaxon();
+                                 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);
+                                 inferredSynonyms.add(inferredGenus);
+                                 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
+                                  taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
+                             }
+                         }
+                         if (!taxonNames.isEmpty()){
+                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                             ZoologicalName name;
+                             if (!synNotInCDM.isEmpty()){
+                                 inferredSynonymsToBeRemoved.clear();
+                                 for (Synonym syn :inferredSynonyms){
+                                     name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                     if (!synNotInCDM.contains(name.getNameCache())){
+                                         inferredSynonymsToBeRemoved.add(syn);
+                                     }
+                                 }
+                                 // Remove identified Synonyms from inferredSynonyms
+                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                     inferredSynonyms.remove(synonym);
+                                 }
+                             }
+                         }
+                     }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
+                         Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
+                         ZoologicalName inferredSynName;
+                         //for all synonyms of the parent...
+                         for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
+                             TaxonNameBase synName;
+                             Synonym synParent = synonymRelationOfParent.getSynonym();
+                             synName = synParent.getName();
+                             HibernateProxyHelper.deproxy(synParent);
+                             // Set the sourceReference
+                             sourceReference = synParent.getSec();
+                             // Determine the idInSource
+                             String idInSourceParent = getIdInSource(synParent);
+                             ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
+                             String synParentGenus = parentSynZooName.getGenusOrUninomial();
+                             String synParentInfragenericName = null;
+                             String synParentSpecificEpithet = null;
+                             if (parentSynZooName.isInfraGeneric()){
+                                 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
+                             }
+                             if (parentSynZooName.isSpecies()){
+                                 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
+                             }
+                            /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
+                                 synonymsGenus.put(synGenusName, idInSource);
+                             }*/
+                             //for all synonyms of the taxon
+                             for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
+                                 Synonym syn = synonymRelationOfTaxon.getSynonym();
+                                 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
+                                         synParentGenus,
+                                         synParentInfragenericName,
+                                         synParentSpecificEpithet, syn, zooHashMap);
+                                 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
+                                 inferredSynonyms.add(potentialCombination);
+                                 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
+                                  taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
+                             }
+                         }
+                         if (doWithMisappliedNames){
+                             for (TaxonRelationship parentRelationship: taxonRelListParent){
+                                 TaxonNameBase misappliedParentName;
+                                 Taxon misappliedParent = parentRelationship.getFromTaxon();
+                                 misappliedParentName = misappliedParent.getName();
+                                 HibernateProxyHelper.deproxy(misappliedParent);
+                                 // Set the sourceReference
+                                 sourceReference = misappliedParent.getSec();
+                                 // Determine the idInSource
+                                 String idInSourceParent = getIdInSource(misappliedParent);
+                                 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
+                                 String synParentGenus = parentSynZooName.getGenusOrUninomial();
+                                 String synParentInfragenericName = null;
+                                 String synParentSpecificEpithet = null;
+                                 if (parentSynZooName.isInfraGeneric()){
+                                     synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
+                                 }
+                                 if (parentSynZooName.isSpecies()){
+                                     synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
+                                 }
+                                 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
+                                     Taxon misappliedName = taxonRelationship.getFromTaxon();
+                                     ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
+                                     potentialCombination = createPotentialCombination(
+                                             idInSourceParent, parentSynZooName, zooMisappliedName,
+                                             synParentGenus,
+                                             synParentInfragenericName,
+                                             synParentSpecificEpithet, misappliedName, zooHashMap);
+                                     taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
+                                     inferredSynonyms.add(potentialCombination);
+                                     zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
+                                      taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
+                                 }
+                             }
+                         }
+                         if (!taxonNames.isEmpty()){
+                             List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
+                             ZoologicalName name;
+                             if (!synNotInCDM.isEmpty()){
+                                 inferredSynonymsToBeRemoved.clear();
+                                 for (Synonym syn :inferredSynonyms){
+                                     try{
+                                         name = (ZoologicalName) syn.getName();
+                                     }catch (ClassCastException e){
+                                         name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
+                                     }
+                                     if (!synNotInCDM.contains(name.getNameCache())){
+                                         inferredSynonymsToBeRemoved.add(syn);
+                                     }
+                                  }
+                                 // Remove identified Synonyms from inferredSynonyms
+                                 for (Synonym synonym : inferredSynonymsToBeRemoved) {
+                                     inferredSynonyms.remove(synonym);
+                                 }
+                             }
+                          }
+                         }
+                     }else {
+                         logger.info("The synonymrelationship type is not defined.");
+                         return inferredSynonyms;
+                     }
+                 }
+             }
+         }
+         return inferredSynonyms;
+     }
+     private Synonym createPotentialCombination(String idInSourceParent,
+             ZoologicalName parentSynZooName,  ZoologicalName zooSynName, String synParentGenus,
+             String synParentInfragenericName, String synParentSpecificEpithet,
+             TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
+         Synonym potentialCombination;
+         Reference sourceReference;
+         ZoologicalName inferredSynName;
+         HibernateProxyHelper.deproxy(syn);
+         // Set sourceReference
+         sourceReference = syn.getSec();
+         if (sourceReference == null){
+             logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
+             //TODO:Remove
+             if (!parentSynZooName.getTaxa().isEmpty()){
+                 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
+                 sourceReference = taxon.getSec();
+             }
+         }
+         String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
+         String synTaxonInfraSpecificName= null;
+         if (parentSynZooName.isSpecies()){
+             synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
+         }
+         /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
+             synonymsEpithet.add(epithetName);
+         }*/
+         //create potential combinations...
+         inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
+         inferredSynName.setGenusOrUninomial(synParentGenus);
+         if (zooSynName.isSpecies()){
+               inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
+               if (parentSynZooName.isInfraGeneric()){
+                   inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
+               }
+         }
+         if (zooSynName.isInfraSpecific()){
+             inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
+             inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
+         }
+         if (parentSynZooName.isInfraGeneric()){
+             inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
+         }
+         potentialCombination = Synonym.NewInstance(inferredSynName, null);
+         // Set the sourceReference
+         potentialCombination.setSec(sourceReference);
+         // Determine the idInSource
+         String idInSourceSyn= getIdInSource(syn);
+         if (idInSourceParent != null && idInSourceSyn != null) {
+             IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
+             inferredSynName.addSource(originalSource);
+             originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
+             potentialCombination.addSource(originalSource);
+         }
+         return potentialCombination;
+     }
+     private Synonym createInferredGenus(Taxon taxon,
+             HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
+             String epithetOfTaxon, String genusOfTaxon,
+             List<String> taxonNames, ZoologicalName zooParentName,
+             TaxonBase syn) {
+         Synonym inferredGenus;
+         TaxonNameBase synName;
+         ZoologicalName inferredSynName;
+         synName =syn.getName();
+         HibernateProxyHelper.deproxy(syn);
+         // Determine the idInSource
+         String idInSourceSyn = getIdInSource(syn);
+         String idInSourceTaxon = getIdInSource(taxon);
+         // Determine the sourceReference
+         Reference sourceReference = syn.getSec();
+         //logger.warn(sourceReference.getTitleCache());
+         synName = syn.getName();
+         ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
+         String synSpeciesEpithetName = synZooName.getSpecificEpithet();
+                      /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
+             synonymsEpithet.add(synSpeciesEpithetName);
+         }*/
+         inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
+         //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
+         inferredSynName.setGenusOrUninomial(genusOfTaxon);
+         if (zooParentName.isInfraGeneric()){
+             inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
+         }
+         if (taxonName.isSpecies()){
+             inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
+         }
+         if (taxonName.isInfraSpecific()){
+             inferredSynName.setSpecificEpithet(epithetOfTaxon);
+             inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
+         }
+         inferredGenus = Synonym.NewInstance(inferredSynName, null);
+         // Set the sourceReference
+         inferredGenus.setSec(sourceReference);
+         // Add the original source
+         if (idInSourceSyn != null && idInSourceTaxon != null) {
+             IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                     idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
+             inferredGenus.addSource(originalSource);
+             originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                     idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
+             inferredSynName.addSource(originalSource);
+             originalSource = null;
+         }else{
+             logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
+             IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                     idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
+             inferredGenus.addSource(originalSource);
+             originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                     idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
+             inferredSynName.addSource(originalSource);
+             originalSource = null;
+         }
+         taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
+         return inferredGenus;
+     }
+     private Synonym createInferredEpithets(Taxon taxon,
+             HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
+             String epithetOfTaxon, String infragenericEpithetOfTaxon,
+             String infraspecificEpithetOfTaxon, List<String> taxonNames,
+             TaxonNameBase parentName, TaxonBase syn) {
+         Synonym inferredEpithet;
+         TaxonNameBase<?,?> synName;
+         ZoologicalName inferredSynName;
+         HibernateProxyHelper.deproxy(syn);
+         // Determine the idInSource
+         String idInSourceSyn = getIdInSource(syn);
+         String idInSourceTaxon =  getIdInSource(taxon);
+         // Determine the sourceReference
+         Reference<?> sourceReference = syn.getSec();
+         if (sourceReference == null){
+              logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
+              sourceReference = taxon.getSec();
+         }
+         synName = syn.getName();
+         ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
+         String synGenusName = zooSynName.getGenusOrUninomial();
+         String synInfraGenericEpithet = null;
+         String synSpecificEpithet = null;
+         if (zooSynName.getInfraGenericEpithet() != null){
+             synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
+         }
+         if (zooSynName.isInfraSpecific()){
+             synSpecificEpithet = zooSynName.getSpecificEpithet();
+         }
+                      /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
+             synonymsGenus.put(synGenusName, idInSource);
+         }*/
+         inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
+         // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
+         if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
+             logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
+         }
+         inferredSynName.setGenusOrUninomial(synGenusName);
+         if (parentName.isInfraGeneric()){
+             inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
+         }
+         if (taxonName.isSpecies()){
+             inferredSynName.setSpecificEpithet(epithetOfTaxon);
+         }else if (taxonName.isInfraSpecific()){
+             inferredSynName.setSpecificEpithet(synSpecificEpithet);
+             inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
+         }
+         inferredEpithet = Synonym.NewInstance(inferredSynName, null);
+         // Set the sourceReference
+         inferredEpithet.setSec(sourceReference);
+         /* Add the original source
+         if (idInSource != null) {
+             IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
+             // Add the citation
+             Reference citation = getCitation(syn);
+             if (citation != null) {
+                 originalSource.setCitation(citation);
+                 inferredEpithet.addSource(originalSource);
+             }
+         }*/
+         String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
+         IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                 taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
+         inferredEpithet.addSource(originalSource);
+         originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
+                 taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
+         inferredSynName.addSource(originalSource);
+         taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
+         return inferredEpithet;
+     }
+     /**
+      * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
+      * Very likely only useful for createInferredSynonyms().
+      * @param uuid
+      * @param zooHashMap
+      * @return
+      */
+     private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
+         ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
+         if (taxonName == null) {
+             taxonName = zooHashMap.get(uuid);
+         }
+         return taxonName;
+     }
+     /**
+      * Returns the idInSource for a given Synonym.
+      * @param syn
+      */
+     private String getIdInSource(TaxonBase taxonBase) {
+         String idInSource = null;
+         Set<IdentifiableSource> sources = taxonBase.getSources();
+         if (sources.size() == 1) {
+             IdentifiableSource source = sources.iterator().next();
+             if (source != null) {
+                 idInSource  = source.getIdInSource();
+             }
+         } else if (sources.size() > 1) {
+             int count = 1;
+             idInSource = "";
+             for (IdentifiableSource source : sources) {
+                 idInSource += source.getIdInSource();
+                 if (count < sources.size()) {
+                     idInSource += "; ";
+                 }
+                 count++;
+             }
+         } else if (sources.size() == 0){
+             logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
+         }
+         return idInSource;
+     }
+     /**
+      * Returns the citation for a given Synonym.
+      * @param syn
+      */
+     private Reference getCitation(Synonym syn) {
+         Reference citation = null;
+         Set<IdentifiableSource> sources = syn.getSources();
+         if (sources.size() == 1) {
+             IdentifiableSource source = sources.iterator().next();
+             if (source != null) {
+                 citation = source.getCitation();
+             }
+         } else if (sources.size() > 1) {
+             logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
+         }
+         return citation;
+     }
+     @Override
+     public List<Synonym>  createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
+         List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
+         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
+         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
+         inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
+         return inferredSynonyms;
+     }
+     @Override
+     public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {
+         // TODO quickly implemented, create according dao !!!!
+         Set<TaxonNode> nodes = new HashSet<TaxonNode>();
+         Set<Classification> classifications = new HashSet<Classification>();
+         List<Classification> list = new ArrayList<Classification>();
+         if (taxonBase == null) {
+             return list;
+         }
+         taxonBase = load(taxonBase.getUuid());
+         if (taxonBase instanceof Taxon) {
+             nodes.addAll(((Taxon)taxonBase).getTaxonNodes());
+         } else {
+             for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {
+                 nodes.addAll(taxon.getTaxonNodes());
+             }
+         }
+         for (TaxonNode node : nodes) {
+             classifications.add(node.getClassification());
+         }
+         list.addAll(classifications);
+         return list;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public UpdateResult changeRelatedTaxonToSynonym(UUID fromTaxonUuid,
+             UUID toTaxonUuid,
+             TaxonRelationshipType oldRelationshipType,
+             SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {
+         UpdateResult result = new UpdateResult();
+         Taxon fromTaxon = (Taxon) dao.load(fromTaxonUuid);
+         Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
+         Synonym synonym = changeRelatedTaxonToSynonym(fromTaxon, toTaxon, oldRelationshipType, synonymRelationshipType);
+         result.setCdmEntity(synonym);
+         result.addUpdatedObject(fromTaxon);
+         result.addUpdatedObject(toTaxon);
+         result.addUpdatedObject(synonym);
+         return result;
+     }
+     @Override
+     @Transactional(readOnly = false)
+     public Synonym changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,
+             SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {
+         // Create new synonym using concept name
+                 TaxonNameBase<?, ?> synonymName = fromTaxon.getName();
+                 Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());
+                 // Remove concept relation from taxon
+                 toTaxon.removeTaxon(fromTaxon, oldRelationshipType);
+                 // Create a new synonym for the taxon
+                 SynonymRelationship synonymRelationship;
+                 if (synonymRelationshipType != null
+                         && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
+                     synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null);
+                 } else{
+                     synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName);
+                 }
+                 this.saveOrUpdate(toTaxon);
+                 //TODO: configurator and classification
+                 TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();
+                 config.setDeleteNameIfPossible(false);
+                 this.deleteTaxon(fromTaxon.getUuid(), config, null);
+                 return synonymRelationship.getSynonym();
+     }
+     @Override
+     public DeleteResult isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){
+         DeleteResult result = new DeleteResult();
+         Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);
+         if (taxonBase instanceof Taxon){
+             TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;
+             result = isDeletableForTaxon(references, taxonConfig);
+         }else{
+             SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;
+             result = isDeletableForSynonym(references, synonymConfig);
+         }
+         return result;
+     }
+     private DeleteResult isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){
+         String message;
+         DeleteResult result = new DeleteResult();
+         for (CdmBase ref: references){
+             if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase )){
+                 message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();
+                 result.addException(new ReferencedObjectUndeletableException(message));
+                 result.addRelatedObject(ref);
+                 result.setAbort();
+             }
+         }
+         return result;
+     }
+     private DeleteResult isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){
+         String message = null;
+         DeleteResult result = new DeleteResult();
+         for (CdmBase ref: references){
+             if (!(ref instanceof TaxonNameBase)){
+               message = null;
+                 if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){
+                     message = "The taxon can't be deleted as long as it has synonyms.";
+                 }
+                 if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){
+                     message = "The taxon can't be deleted as long as it has factual data.";
+                 }
+                 if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){
+                     message = "The taxon can't be deleted as long as it belongs to a taxon node.";
+                 }
+                 if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){
+                     if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){
+                         message = "The taxon can't be deleted as long as it has misapplied names or invalid designations.";
+                     } else{
+                         message = "The taxon can't be deleted as long as it belongs to a taxon node.";
+                     }
+                 }
+                 if (ref instanceof PolytomousKeyNode){
+                     message = "The taxon can't be deleted as long as it is referenced by a polytomous key node.";
+                 }
+                 if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){
+                    message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this taxon";
+                 }
+                /* //PolytomousKeyNode
+                 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
+                     String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
+                     return message;
+                 }*/
+                 //TaxonInteraction
+                 if (ref.isInstanceOf(TaxonInteraction.class)){
+                     message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
+                 }
+               //TaxonInteraction
+                 if (ref.isInstanceOf(DeterminationEvent.class)){
+                     message = "Taxon can't be deleted as it is used in a determination event";
+                 }
+             }
+             if (message != null){
+                   result.addException(new ReferencedObjectUndeletableException(message));
+                   result.addRelatedObject(ref);
+                   result.setAbort();
+             }
+         }
+         return result;
+     }
+     @Override
+     public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) {
+         IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid);
+         //preliminary implementation
+         Set<Taxon> taxa = new HashSet<Taxon>();
+         TaxonBase taxonBase = find(taxonUuid);
+         if (taxonBase == null){
+             return new IncludedTaxaDTO();
+         }else if (taxonBase.isInstanceOf(Taxon.class)){
+             Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
+             taxa.add(taxon);
+         }else if (taxonBase.isInstanceOf(Synonym.class)){
+             //TODO partial synonyms ??
+             //TODO synonyms in general
+             Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);
+             taxa.addAll(syn.getAcceptedTaxa());
+         }else{
+             throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName());
+         }
+         Set<Taxon> related = makeRelatedIncluded(taxa, result, config);
+         int i = 0;
+         while((! related.isEmpty()) && i++ < 100){  //to avoid
+              related = makeRelatedIncluded(related, result, config);
+         }
+         return result;
+     }
+     /**
+      * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
+      * data structure.
+      * @return the set of conceptually related taxa for further use
+      */
+     /**
+      * @param uncheckedTaxa
+      * @param existingTaxa
+      * @param config
+      * @return
+      */
+     private Set<Taxon> makeRelatedIncluded(Set<Taxon> uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {
+         //children
+         Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();
+         for (Taxon taxon: uncheckedTaxa){
+             taxonNodes.addAll(taxon.getTaxonNodes());
+         }
+         Set<Taxon> children = new HashSet<Taxon>();
+         if (! config.onlyCongruent){
+             for (TaxonNode node: taxonNodes){
+                 List<TaxonNode> childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, null);
+                 for (TaxonNode child : childNodes){
+                     children.add(child.getTaxon());
+                 }
+             }
+             children.remove(null);  // just to be on the save side
+         }
+         Iterator<Taxon> it = children.iterator();
+         while(it.hasNext()){
+             UUID uuid = it.next().getUuid();
+             if (existingTaxa.contains(uuid)){
+                 it.remove();
+             }else{
+                 existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);
+             }
+         }
+         //concept relations
+         Set<Taxon> uncheckedAndChildren = new HashSet<Taxon>(uncheckedTaxa);
+         uncheckedAndChildren.addAll(children);
+         Set<Taxon> relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config);
+         Set<Taxon> result = new HashSet<Taxon>(relatedTaxa);
+         return result;
+     }
+     /**
+      * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
+      * @return the set of these computed taxa
+      */
+     private Set<Taxon> makeConceptIncludedTaxa(Set<Taxon> unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {
+         Set<Taxon> result = new HashSet<Taxon>();
+         for (Taxon taxon : unchecked){
+             Set<TaxonRelationship> fromRelations = taxon.getRelationsFromThisTaxon();
+             Set<TaxonRelationship> toRelations = taxon.getRelationsToThisTaxon();
+             for (TaxonRelationship fromRel : fromRelations){
+                 if (config.includeDoubtful == false && fromRel.isDoubtful()){
+                     continue;
+                 }
+                 if (fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO()) ||
+                         !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.INCLUDES()) ||
+                         !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES())
+                         ){
+                     result.add(fromRel.getToTaxon());
+                 }
+             }
+             for (TaxonRelationship toRel : toRelations){
+                 if (config.includeDoubtful == false && toRel.isDoubtful()){
+                     continue;
+                 }
+                 if (toRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){
+                     result.add(toRel.getFromTaxon());
+                 }
+             }
+         }
+         Iterator<Taxon> it = result.iterator();
+         while(it.hasNext()){
+             UUID uuid = it.next().getUuid();
+             if (existingTaxa.contains(uuid)){
+                 it.remove();
+             }else{
+                 existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);
+             }
+         }
+         return result;
+     }
+     @Override
+     public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){
+         List<TaxonBase> taxonList = dao.getTaxaByName(true, false, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, 0, config.getPropertyPath());
+         return taxonList;
+     }
+       @Override
+       @Transactional(readOnly = true)
+       public <S extends TaxonBase> Pager<FindByIdentifierDTO<S>> findByIdentifier(
+                       Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
+                       MatchMode matchmode, boolean includeEntity, Integer pageSize,
+                       Integer pageNumber,     List<String> propertyPaths) {
+               if (subtreeFilter == null){
+                       return findByIdentifier(clazz, identifier, identifierType, matchmode, includeEntity, pageSize, pageNumber, propertyPaths);
+               }
+               Integer numberOfResults = dao.countByIdentifier(clazz, identifier, identifierType, subtreeFilter, matchmode);
+         List<Object[]> daoResults = new ArrayList<Object[]>();
+         if(numberOfResults > 0) { // no point checking again
+               daoResults = dao.findByIdentifier(clazz, identifier, identifierType, subtreeFilter,
+                               matchmode, includeEntity, pageSize, pageNumber, propertyPaths);
+         }
+         List<FindByIdentifierDTO<S>> result = new ArrayList<FindByIdentifierDTO<S>>();
+         for (Object[] daoObj : daoResults){
+               if (includeEntity){
+                       result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (S)daoObj[2]));
+               }else{
+                       result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));
+               }
+         }
+               return new DefaultPagerImpl<FindByIdentifierDTO<S>>(pageNumber, numberOfResults, pageSize, result);
+       }
+       @Override
+       @Transactional(readOnly = false)
+       public UpdateResult moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, UUID newTaxonUUID, boolean moveHomotypicGroup,
+             SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
+           UpdateResult result = new UpdateResult();
+               Taxon newTaxon = (Taxon) dao.load(newTaxonUUID);
+               SynonymRelationship sr = moveSynonymToAnotherTaxon(oldSynonymRelation, newTaxon, moveHomotypicGroup, newSynonymRelationshipType, reference, referenceDetail, keepReference);
+               result.setCdmEntity(sr);
+               result.addUpdatedObject(sr);
+               result.addUpdatedObject(newTaxon);
+               return result;
+       }
+       @Override
+       public UpdateResult moveFactualDateToAnotherTaxon(UUID fromTaxonUuid, UUID toTaxonUuid){
+               UpdateResult result = new UpdateResult();
+               Taxon fromTaxon = (Taxon)dao.load(fromTaxonUuid);
+               Taxon toTaxon = (Taxon) dao.load(toTaxonUuid);
+                 for(TaxonDescription description : fromTaxon.getDescriptions()){
+               //reload to avoid session conflicts
+               description = HibernateProxyHelper.deproxy(description, TaxonDescription.class);
+               String moveMessage = String.format("Description moved from %s", fromTaxon);
+               if(description.isProtectedTitleCache()){
+                   String separator = "";
+                   if(!StringUtils.isBlank(description.getTitleCache())){
+                       separator = " - ";
+                   }
+                   description.setTitleCache(description.getTitleCache() + separator + moveMessage, true);
+               }
+               Annotation annotation = Annotation.NewInstance(moveMessage, Language.getDefaultLanguage());
+               annotation.setAnnotationType(AnnotationType.TECHNICAL());
+               description.addAnnotation(annotation);
+               toTaxon.addDescription(description);
+               dao.saveOrUpdate(toTaxon);
+               dao.saveOrUpdate(fromTaxon);
+               result.addUpdatedObject(toTaxon);
+               result.addUpdatedObject(fromTaxon);
+           }
+               return result;
+       }
+       @Override
+       public DeleteResult deleteSynonym(UUID synonymUuid, UUID taxonUuid,
+                       SynonymDeletionConfigurator config) {
+               TaxonBase base = this.load(synonymUuid);
+               Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);
+               base = this.load(taxonUuid);
+               Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);
+               return this.deleteSynonym(syn, taxon, config);
+       }
+       @Override
+       @Transactional(readOnly = false)
+       public UpdateResult swapSynonymAndAcceptedTaxon(UUID synonymUUid,
+                       UUID acceptedTaxonUuid) {
+               TaxonBase base = this.load(synonymUUid);
+               Synonym syn = HibernateProxyHelper.deproxy(base, Synonym.class);
+               base = this.load(acceptedTaxonUuid);
+               Taxon taxon = HibernateProxyHelper.deproxy(base, Taxon.class);
+               return this.swapSynonymAndAcceptedTaxon(syn, taxon);
+       }
+ }
index 0eaa5ab7ad13eea1d4e04c933f8d4be8bf6651cc,007777989e09e08a45526745c835c054480e94bd..b7553f0578f4922b8546f808c8f97c0a5883dd0b
- /**\r
- * Copyright (C) 2009 EDIT\r
- * European Distributed Institute of Taxonomy\r
- * http://www.e-taxonomy.eu\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version 1.1\r
- * See LICENSE.TXT at the top of this package for the full license terms.\r
- */\r
\r
- package eu.etaxonomy.cdm.api.service;\r
\r
- import static org.junit.Assert.assertNotNull;\r
- import static org.junit.Assert.assertNull;\r
\r
- import java.io.FileNotFoundException;\r
- import java.util.Collection;\r
- import java.util.HashSet;\r
- import java.util.Iterator;\r
- import java.util.Map.Entry;\r
- import java.util.Set;\r
- import java.util.UUID;\r
\r
- import org.apache.log4j.Logger;\r
- import org.junit.Assert;\r
- import org.junit.Test;\r
- import org.unitils.dbunit.annotation.DataSet;\r
- import org.unitils.spring.annotation.SpringBeanByType;\r
\r
- import eu.etaxonomy.cdm.api.service.pager.Pager;\r
- import eu.etaxonomy.cdm.model.common.Language;\r
- import eu.etaxonomy.cdm.model.common.LanguageString;\r
- import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
- import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
- import eu.etaxonomy.cdm.model.description.Feature;\r
- import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
- import eu.etaxonomy.cdm.model.description.TextData;\r
- import eu.etaxonomy.cdm.model.taxon.Taxon;\r
- import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;\r
\r
- /**\r
-  * @author a.babadshanjan\r
-  * @created 09.02.2009\r
-  * @version 1.0\r
-  */\r
\r
- public class DescriptionServiceImplTest extends CdmTransactionalIntegrationTest {\r
-     @SuppressWarnings("unused")\r
-     private static Logger logger = Logger.getLogger(DescriptionServiceImplTest.class);\r
\r
-     @SpringBeanByType\r
-     private IDescriptionService service;\r
\r
-     @SpringBeanByType\r
-     private ITermService termService;\r
\r
\r
\r
-     @Test\r
-     public void testGetDefaultFeatureVocabulary() {\r
-         service.getDefaultFeatureVocabulary();\r
-     }\r
\r
-     @Test\r
-     @DataSet("CommonServiceImplTest.xml")\r
-     public void testChangeDescriptionElement(){\r
-         DescriptionBase<?> descBase = service.find(UUID.fromString("eb17b80a-9be6-4642-a6a8-b19a318925e6"));\r
-         Set<DescriptionElementBase> elements = descBase.getElements();\r
-         Iterator<?> iterator = elements.iterator();\r
-         while (iterator.hasNext()){\r
-             DescriptionElementBase base = (DescriptionElementBase) iterator.next();\r
-             if (base instanceof TextData){\r
-                 TextData textdata = (TextData) base;\r
-                 Set <Entry<Language,LanguageString>> entries = textdata.getMultilanguageText().entrySet();\r
-                 Iterator<?> entryIterator = entries.iterator();\r
-                 while (entryIterator.hasNext()){\r
-                     Entry <Language, LanguageString> entry = (Entry<Language, LanguageString>) entryIterator.next();\r
-                     LanguageString langString = entry.getValue();\r
- //                                    System.out.println(langString);\r
-                     langString.setText("blablubber");\r
-                 }\r
-             }\r
\r
-         }\r
-         service.saveOrUpdate(descBase);\r
-         Pager<DescriptionElementBase> allElements = service.getDescriptionElements(null, null, null, null, null, null);\r
-         Assert.assertEquals(1, allElements.getCount().intValue());\r
-         DescriptionElementBase test = allElements.getRecords().get(0);\r
-         if (test instanceof TextData){\r
\r
-             Set <Entry<Language,LanguageString>> entries = ((TextData) test).getMultilanguageText().entrySet();\r
-             Iterator<Entry<Language,LanguageString>> entryIterator = entries.iterator();\r
-             while (entryIterator.hasNext()){\r
-                 Entry <Language, LanguageString> entry = entryIterator.next();\r
-                 LanguageString langString = entry.getValue();\r
- //                            System.out.println(langString);\r
-             }\r
-         }\r
-     }\r
\r
\r
-     @Test\r
-     public void testMoveDescriptionElement(){\r
-         //Create data\r
-         Taxon taxon = Taxon.NewInstance(null, null);\r
-         TaxonDescription desc1 = TaxonDescription.NewInstance(taxon);\r
-         TextData textData1 = TextData.NewInstance(Feature.HABITAT(), "My habitat", Language.GERMAN(), null);\r
-         desc1.addElement(textData1);\r
-         service.saveOrUpdate(desc1);\r
\r
-         TaxonDescription desc2 = TaxonDescription.NewInstance(taxon);\r
-         TextData textData2 = TextData.NewInstance(Feature.HABITAT(), "My habitat2", Language.GERMAN(), null);\r
-         desc2.addElement(textData2);\r
-         service.saveOrUpdate(desc2);\r
-         commitAndStartNewTransaction(null);\r
\r
\r
-         DescriptionBase<?> descLoaded1 = service.find(desc1.getUuid());\r
-         DescriptionBase<?> descLoaded2 = service.find(desc2.getUuid());\r
\r
-         DescriptionElementBase textDataLoaded = descLoaded1.getElements().iterator().next();\r
-         Set<DescriptionElementBase> tmpSet = new HashSet<DescriptionElementBase>(descLoaded1.getElements());\r
\r
-         //test for #4806\r
-         service.moveDescriptionElementsToDescription(tmpSet, descLoaded2, false);\r
-         try {\r
-             commitAndStartNewTransaction(null);\r
-         } catch (Exception e) {\r
-             Assert.fail("Moving description element should not throw an exception. Exception is " + e.getMessage());\r
-         }\r
\r
\r
-     }\r
\r
-     @Test\r
-     public void testMoveDescriptionElementsToTaxon(){\r
-         //Create data\r
-         UUID commonNameFeatureUuid = Feature.COMMON_NAME().getUuid();\r
-         Feature commonNameFeatureData = (Feature)termService.find(commonNameFeatureUuid);\r
\r
-         TaxonDescription sourceDescriptionData = TaxonDescription.NewInstance();\r
-         TextData elementData = TextData.NewInstance();\r
-         elementData.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(elementData);\r
\r
-         TextData element2 = TextData.NewInstance();\r
-         element2.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(element2);\r
\r
-         TextData element3Data = TextData.NewInstance();\r
-         element3Data.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(element3Data);\r
-         Assert.assertEquals(3, sourceDescriptionData.getElements().size());\r
-         TaxonDescription targetDescriptionData = TaxonDescription.NewInstance();\r
-         this.service.save(sourceDescriptionData);\r
-         this.service.save(targetDescriptionData);\r
\r
-         commitAndStartNewTransaction(null);\r
\r
-         TaxonDescription sourceDescription = (TaxonDescription)this.service.find(sourceDescriptionData.getId());\r
-         Assert.assertEquals(3, sourceDescription.getElements().size());\r
\r
-         TaxonDescription targetDescription = (TaxonDescription)this.service.find(targetDescriptionData.getId());\r
\r
\r
-         Collection<DescriptionElementBase> sourceCollection = new HashSet<DescriptionElementBase>();\r
-         sourceCollection.addAll(sourceDescription.getElements());\r
-         sourceCollection.remove(element3Data);  //should work as it works on equal\r
-         Assert.assertEquals(2, sourceCollection.size());\r
\r
-         service.moveDescriptionElementsToDescription(sourceCollection, targetDescription, false);\r
\r
-         Assert.assertEquals("Source description should have 1 element left", 1, sourceDescription.getElements().size());\r
-         Assert.assertEquals("Target description should have 2 new elements", 2, targetDescription.getElements().size());\r
-         //the following tests are not valid anymore as elements are cloned now even if isCopy is false\r
-         //            Assert.assertTrue("The moved element should be in the new description", targetDescription.getElements().contains(element));\r
-         //            Assert.assertTrue("The moved element2 should be in the new description", targetDescription.getElements().contains(element2));\r
-         //            Assert.assertFalse("Element3 should not be in the new description", targetDescription.getElements().contains(element3));\r
\r
-         Assert.assertTrue("Element3 should remain in the old description", sourceDescription.getElements().contains(element3Data));\r
-         sourceDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());\r
-         targetDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());\r
-         assertNotNull(sourceDescription);\r
-         assertNotNull(targetDescription);\r
-         try {\r
-             service.moveDescriptionElementsToDescription(targetDescription.getElements(), sourceDescription, false);\r
-         } catch (Exception e) {\r
-             //asserting that no ConcurrentModificationException is thrown when the elements collection is passed as a parameter\r
-             e.printStackTrace();\r
-             Assert.fail();\r
-         }\r
\r
-         Assert.assertEquals("Source description should have 3 elements again", 3, sourceDescription.getElements().size());\r
-         Assert.assertEquals("Destination description should have no elements again", 0, targetDescription.getElements().size());\r
-         sourceDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());\r
-         targetDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());\r
-         assertNotNull(sourceDescription);\r
-         assertNull(targetDescription);\r
\r
\r
\r
\r
-     }\r
\r
-     @Test\r
-     public void testMoveDescriptionElementsToTaxonAndResaveDeletedDescription(){\r
\r
-       //Create data\r
-         UUID commonNameFeatureUuid = Feature.COMMON_NAME().getUuid();\r
-         Feature commonNameFeatureData = (Feature)termService.find(commonNameFeatureUuid);\r
\r
-         TaxonDescription sourceDescriptionData = TaxonDescription.NewInstance();\r
-         TextData elementData = TextData.NewInstance();\r
-         elementData.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(elementData);\r
\r
-         TextData element2 = TextData.NewInstance();\r
-         element2.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(element2);\r
\r
-         TextData element3Data = TextData.NewInstance();\r
-         element3Data.setFeature(commonNameFeatureData);\r
-         sourceDescriptionData.addElement(element3Data);\r
-         Assert.assertEquals(3, sourceDescriptionData.getElements().size());\r
-         TaxonDescription targetDescriptionData = TaxonDescription.NewInstance();\r
-         this.service.save(sourceDescriptionData);\r
-         this.service.save(targetDescriptionData);\r
\r
-         commitAndStartNewTransaction(null);\r
\r
-         TaxonDescription sourceDescription = (TaxonDescription)this.service.find(sourceDescriptionData.getId());\r
-         Assert.assertEquals(3, sourceDescription.getElements().size());\r
\r
-         TaxonDescription targetDescription = (TaxonDescription)this.service.find(targetDescriptionData.getId());\r
-         service.moveDescriptionElementsToDescription(sourceDescription.getElements(), targetDescription, false);\r
-         TaxonDescription removedDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());\r
-         assertNull(removedDescription);\r
-         this.service.save(targetDescription);\r
\r
-         removedDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());\r
-         assertNotNull(removedDescription);\r
-     }\r
\r
-     @Override\r
-     public void createTestDataSet() throws FileNotFoundException {};\r
-     }\r
+ /**
+ * Copyright (C) 2009 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+ package eu.etaxonomy.cdm.api.service;
++import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertNull;
++
+ import java.io.FileNotFoundException;
+ import java.util.Collection;
+ import java.util.HashSet;
+ import java.util.Iterator;
+ import java.util.Map.Entry;
+ import java.util.Set;
+ import java.util.UUID;
+ import org.apache.log4j.Logger;
+ import org.junit.Assert;
+ import org.junit.Ignore;
+ import org.junit.Test;
+ import org.unitils.dbunit.annotation.DataSet;
+ import org.unitils.spring.annotation.SpringBeanByType;
+ import eu.etaxonomy.cdm.api.service.pager.Pager;
+ import eu.etaxonomy.cdm.model.common.Language;
+ import eu.etaxonomy.cdm.model.common.LanguageString;
+ import eu.etaxonomy.cdm.model.description.DescriptionBase;
+ import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+ import eu.etaxonomy.cdm.model.description.Feature;
+ import eu.etaxonomy.cdm.model.description.TaxonDescription;
+ import eu.etaxonomy.cdm.model.description.TextData;
+ import eu.etaxonomy.cdm.model.taxon.Taxon;
+ import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
+ /**
+  * @author a.babadshanjan
+  * @created 09.02.2009
+  * @version 1.0
+  */
+ public class DescriptionServiceImplTest extends CdmTransactionalIntegrationTest {
+     @SuppressWarnings("unused")
+     private static Logger logger = Logger.getLogger(DescriptionServiceImplTest.class);
+     @SpringBeanByType
+     private IDescriptionService service;
+     @SpringBeanByType
+     private ITermService termService;
+     @Test
+     public void testGetDefaultFeatureVocabulary() {
+         service.getDefaultFeatureVocabulary();
+     }
+     @Test
+     @DataSet("CommonServiceImplTest.xml")
+     public void testChangeDescriptionElement(){
+         DescriptionBase<?> descBase = service.find(UUID.fromString("eb17b80a-9be6-4642-a6a8-b19a318925e6"));
+         Set<DescriptionElementBase> elements = descBase.getElements();
+         Iterator<?> iterator = elements.iterator();
+         while (iterator.hasNext()){
+             DescriptionElementBase base = (DescriptionElementBase) iterator.next();
+             if (base instanceof TextData){
+                 TextData textdata = (TextData) base;
+                 Set <Entry<Language,LanguageString>> entries = textdata.getMultilanguageText().entrySet();
+                 Iterator<?> entryIterator = entries.iterator();
+                 while (entryIterator.hasNext()){
+                     Entry <Language, LanguageString> entry = (Entry<Language, LanguageString>) entryIterator.next();
+                     LanguageString langString = entry.getValue();
+ //                                    System.out.println(langString);
+                     langString.setText("blablubber");
+                 }
+             }
+         }
+         service.saveOrUpdate(descBase);
+         Pager<DescriptionElementBase> allElements = service.getDescriptionElements(null, null, null, null, null, null);
+         Assert.assertEquals(1, allElements.getCount().intValue());
+         DescriptionElementBase test = allElements.getRecords().get(0);
+         if (test instanceof TextData){
+             Set <Entry<Language,LanguageString>> entries = ((TextData) test).getMultilanguageText().entrySet();
+             Iterator<Entry<Language,LanguageString>> entryIterator = entries.iterator();
+             while (entryIterator.hasNext()){
+                 Entry <Language, LanguageString> entry = entryIterator.next();
+                 LanguageString langString = entry.getValue();
+ //                            System.out.println(langString);
+             }
+         }
+     }
+     @Test
+     public void testMoveDescriptionElement(){
+         //Create data
+         Taxon taxon = Taxon.NewInstance(null, null);
+         TaxonDescription desc1 = TaxonDescription.NewInstance(taxon);
+         TextData textData1 = TextData.NewInstance(Feature.HABITAT(), "My habitat", Language.GERMAN(), null);
+         desc1.addElement(textData1);
+         service.saveOrUpdate(desc1);
+         TaxonDescription desc2 = TaxonDescription.NewInstance(taxon);
+         TextData textData2 = TextData.NewInstance(Feature.HABITAT(), "My habitat2", Language.GERMAN(), null);
+         desc2.addElement(textData2);
+         service.saveOrUpdate(desc2);
+         commitAndStartNewTransaction(null);
+         DescriptionBase<?> descLoaded1 = service.find(desc1.getUuid());
+         DescriptionBase<?> descLoaded2 = service.find(desc2.getUuid());
+         DescriptionElementBase textDataLoaded = descLoaded1.getElements().iterator().next();
+         Set<DescriptionElementBase> tmpSet = new HashSet<DescriptionElementBase>(descLoaded1.getElements());
+         //test for #4806
+         service.moveDescriptionElementsToDescription(tmpSet, descLoaded2, false);
+         try {
+             commitAndStartNewTransaction(null);
+         } catch (Exception e) {
+             Assert.fail("Moving description element should not throw an exception. Exception is " + e.getMessage());
+         }
+     }
+     @Test
+     @Ignore
+     public void testMoveDescriptionElementsToTaxon(){
+         //Create data
+         UUID commonNameFeatureUuid = Feature.COMMON_NAME().getUuid();
+         Feature commonNameFeatureData = (Feature)termService.find(commonNameFeatureUuid);
+         TaxonDescription sourceDescriptionData = TaxonDescription.NewInstance();
+         TextData elementData = TextData.NewInstance();
+         elementData.setFeature(commonNameFeatureData);
+         sourceDescriptionData.addElement(elementData);
+         TextData element2 = TextData.NewInstance();
+         element2.setFeature(commonNameFeatureData);
+         sourceDescriptionData.addElement(element2);
+         TextData element3Data = TextData.NewInstance();
+         element3Data.setFeature(commonNameFeatureData);
+         sourceDescriptionData.addElement(element3Data);
+         Assert.assertEquals(3, sourceDescriptionData.getElements().size());
+         TaxonDescription targetDescriptionData = TaxonDescription.NewInstance();
+         this.service.save(sourceDescriptionData);
+         this.service.save(targetDescriptionData);
+         commitAndStartNewTransaction(null);
+         TaxonDescription sourceDescription = (TaxonDescription)this.service.find(sourceDescriptionData.getId());
+         Assert.assertEquals(3, sourceDescription.getElements().size());
+         TaxonDescription targetDescription = (TaxonDescription)this.service.find(targetDescriptionData.getId());
+         Collection<DescriptionElementBase> sourceCollection = new HashSet<DescriptionElementBase>();
+         sourceCollection.addAll(sourceDescription.getElements());
+         sourceCollection.remove(element3Data);  //should work as it works on equal
+         Assert.assertEquals(2, sourceCollection.size());
+         service.moveDescriptionElementsToDescription(sourceCollection, targetDescription, false);
+         Assert.assertEquals("Source description should have 1 element left", 1, sourceDescription.getElements().size());
+         Assert.assertEquals("Target description should have 2 new elements", 2, targetDescription.getElements().size());
+         //the following tests are not valid anymore as elements are cloned now even if isCopy is false
+         //            Assert.assertTrue("The moved element should be in the new description", targetDescription.getElements().contains(element));
+         //            Assert.assertTrue("The moved element2 should be in the new description", targetDescription.getElements().contains(element2));
+         //            Assert.assertFalse("Element3 should not be in the new description", targetDescription.getElements().contains(element3));
+         Assert.assertTrue("Element3 should remain in the old description", sourceDescription.getElements().contains(element3Data));
 -        this.service.save(sourceDescription);
 -        this.service.save(targetDescription);
 -
++        sourceDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());
++        targetDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());
++        assertNotNull(sourceDescription);
++        assertNotNull(targetDescription);
+         try {
+             service.moveDescriptionElementsToDescription(targetDescription.getElements(), sourceDescription, false);
+         } catch (Exception e) {
+             //asserting that no ConcurrentModificationException is thrown when the elements collection is passed as a parameter
+             e.printStackTrace();
+             Assert.fail();
+         }
+         Assert.assertEquals("Source description should have 3 elements again", 3, sourceDescription.getElements().size());
+         Assert.assertEquals("Destination description should have no elements again", 0, targetDescription.getElements().size());
 -        this.service.save(sourceDescription);
 -        this.service.save(targetDescription);
++        sourceDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());
++        targetDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());
++        assertNotNull(sourceDescription);
++        assertNull(targetDescription);
 -        //test copy
 -        sourceCollection.clear();
 -        sourceCollection.add(sourceDescription.getElements().iterator().next());
 -        service.moveDescriptionElementsToDescription(sourceCollection, targetDescription, true);
 -        Assert.assertEquals("Source description should still have 3 elements", 3, sourceDescription.getElements().size());
 -        int size = targetDescription.getElements().size();
 -        Assert.assertEquals("Destination descirption should have 1 element again", 1, size);
 -        for (DescriptionElementBase targetElement : targetDescription.getElements()){
 -            Assert.assertFalse("Target elements may not be in sourced description as they are only clones (but not same).", sourceDescription.getElements().contains(targetElement));
 -        }
 -        this.service.save(targetDescription);
 -        this.service.save(sourceDescription);
++    }
++
++    @Test
++    public void testMoveDescriptionElementsToTaxonAndResaveDeletedDescription(){
++
++      //Create data
++        UUID commonNameFeatureUuid = Feature.COMMON_NAME().getUuid();
++        Feature commonNameFeatureData = (Feature)termService.find(commonNameFeatureUuid);
++
++        TaxonDescription sourceDescriptionData = TaxonDescription.NewInstance();
++        TextData elementData = TextData.NewInstance();
++        elementData.setFeature(commonNameFeatureData);
++        sourceDescriptionData.addElement(elementData);
++
++        TextData element2 = TextData.NewInstance();
++        element2.setFeature(commonNameFeatureData);
++        sourceDescriptionData.addElement(element2);
++
++        TextData element3Data = TextData.NewInstance();
++        element3Data.setFeature(commonNameFeatureData);
++        sourceDescriptionData.addElement(element3Data);
++        Assert.assertEquals(3, sourceDescriptionData.getElements().size());
++        TaxonDescription targetDescriptionData = TaxonDescription.NewInstance();
++        this.service.save(sourceDescriptionData);
++        this.service.save(targetDescriptionData);
++
++        commitAndStartNewTransaction(null);
++
++        TaxonDescription sourceDescription = (TaxonDescription)this.service.find(sourceDescriptionData.getId());
++        Assert.assertEquals(3, sourceDescription.getElements().size());
++
++        TaxonDescription targetDescription = (TaxonDescription)this.service.find(targetDescriptionData.getId());
++        service.moveDescriptionElementsToDescription(sourceDescription.getElements(), targetDescription, false);
++        TaxonDescription removedDescription = (TaxonDescription) this.service.find(sourceDescription.getUuid());
++        assertNull(removedDescription);
++        this.service.save(targetDescription);
++
++        removedDescription = (TaxonDescription) this.service.find(targetDescription.getUuid());
++        assertNotNull(removedDescription);
+     }
+     @Override
+     public void createTestDataSet() throws FileNotFoundException {};
+     }
diff --cc pom.xml
Simple merge