Merge branch 'develop' of ssh://dev.e-taxonomy.eu/var/git/cdmlib into develop
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / NameServiceImpl.java
index 8b368f52567d0d36b2f7e7c05c6ec6412af90c3a..8f671809852afe20582fbeedaca4e641cc8117fb 100644 (file)
-// $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
+/**
+* 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.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+import org.apache.lucene.index.Term;
+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.dto.TypeDesignationStatusFilter;
+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.LuceneParseException;
+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.utility.TaxonNamePartsFilter;
+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.common.SourcedEntityBase;
+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.INonViralName;
+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.Rank;
+import eu.etaxonomy.cdm.model.name.Registration;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
+import eu.etaxonomy.cdm.model.name.TaxonName;
+import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
+import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
+import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
+import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
+import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
+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.dao.term.IOrderedTermVocabularyDao;
+import eu.etaxonomy.cdm.persistence.dao.term.ITermVocabularyDao;
+import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
+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<TaxonName,ITaxonNameDao>
+          implements INameService {
+    static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
+
+    @Autowired
+    protected ITermVocabularyDao vocabularyDao;
+    @Autowired
+    protected IOrderedTermVocabularyDao orderedVocabularyDao;
+    @Autowired
+    protected IOccurrenceService occurrenceService;
+    @Autowired
+    protected ICollectionService collectionService;
+    @Autowired
+    @Qualifier("refEntDao")
+    protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
+    @Autowired
+    @Qualifier("sourcedEntityDao")
+    protected ISourcedEntityDao<SourcedEntityBase<?>> sourcedEntityDao;
+    @Autowired
+    private INomenclaturalStatusDao nomStatusDao;
+    @Autowired
+    private ITypeDesignationDao typeDesignationDao;
+    @Autowired
+    private IHomotypicalGroupDao homotypicalGroupDao;
+    @Autowired
+    private ICdmGenericDao genericDao;
+    @Autowired
+    private ILuceneIndexToolProvider luceneIndexToolProvider;
+    @Autowired
+    // @Qualifier("defaultBeanInitializer")
+    protected IBeanInitializer defaultBeanInitializer;
+
+    /**
+     * Constructor
+     */
+    public NameServiceImpl(){}
+
+//********************* METHODS ****************************************************************//
+
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult delete(UUID nameUUID){
+        NameDeletionConfigurator config = new NameDeletionConfigurator();
+        DeleteResult result = delete(nameUUID, config);
+        return result;
+    }
+
+    @Override
+    public DeleteResult delete(TaxonName name){
+        return delete(name.getUuid());
+    }
+
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
+        DeleteResult result = new DeleteResult();
+
+        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{
+                dao.delete(name);
+                result.addDeletedObject(name);
+            }catch(Exception e){
+                result.addException(e);
+                result.setError();
+            }
+            return result;
+        }
+
+        return result;
+    }
+
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
+
+        TaxonName name = dao.load(nameUUID);
+        return delete(name, config);
+    }
+
+    @Override
+    @Transactional(readOnly = false)
+    public UpdateResult cloneTypeDesignation(UUID nameUuid, SpecimenTypeDesignation baseDesignation,
+            String accessionNumber, String barcode, String catalogNumber,
+            UUID collectionUuid, SpecimenTypeDesignationStatus typeStatus){
+        UpdateResult result = new UpdateResult();
+
+        DerivedUnit baseSpecimen = HibernateProxyHelper.deproxy(occurrenceService.load(baseDesignation.getTypeSpecimen().getUuid(), Arrays.asList("collection")), DerivedUnit.class);
+        DerivedUnit duplicate = DerivedUnit.NewInstance(baseSpecimen.getRecordBasis());
+        DerivationEvent derivedFrom = baseSpecimen.getDerivedFrom();
+        Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(baseSpecimen.getUuid(), null);
+        if(fieldUnits.size()!=1){
+            result.addException(new Exception("More than one or no field unit found for specimen"));
+            result.setError();
+            return result;
+        }
+        for (SpecimenOrObservationBase original : derivedFrom.getOriginals()) {
+            DerivationEvent.NewSimpleInstance(original, duplicate, derivedFrom.getType());
+        }
+        duplicate.setAccessionNumber(accessionNumber);
+        duplicate.setBarcode(barcode);
+        duplicate.setCatalogNumber(catalogNumber);
+        duplicate.setCollection(collectionService.load(collectionUuid));
+        SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
+        typeDesignation.setTypeSpecimen(duplicate);
+        typeDesignation.setTypeStatus(typeStatus);
+
+        TaxonName name = load(nameUuid);
+        name.getTypeDesignations().add(typeDesignation);
+
+        result.setCdmEntity(typeDesignation);
+        result.addUpdatedObject(name);
+        return result;
+    }
+
+    @Override
+    @Transactional
+    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
+       if(typeDesignation != null && typeDesignation .isPersited()){
+               typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
+       }
+
+        DeleteResult result = new DeleteResult();
+        if (name == null && typeDesignation == null){
+            result.setError();
+            return result;
+        }else if (name != null && typeDesignation != null){
+            removeSingleDesignation(name, typeDesignation);
+        }else if (name != null){
+            @SuppressWarnings("rawtypes")
+            Set<TypeDesignationBase<?>> designationSet = new HashSet(name.getTypeDesignations());
+            for (TypeDesignationBase<?> desig : designationSet){
+                desig = CdmBase.deproxy(desig);
+                removeSingleDesignation(name, desig);
+            }
+        }else if (typeDesignation != null){
+            @SuppressWarnings("unchecked")
+            Set<TaxonName> nameSet = new HashSet(typeDesignation.getTypifiedNames());
+            for (TaxonName singleName : nameSet){
+                singleName = CdmBase.deproxy(singleName);
+                removeSingleDesignation(singleName, typeDesignation);
+            }
+        }
+        result.addDeletedObject(typeDesignation);
+        result.addUpdatedObject(name);
+        return result;
+    }
+
+
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
+        TaxonName nameBase = load(nameUuid);
+        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
+        return deleteTypeDesignation(nameBase, typeDesignation);
+    }
+
+    /**
+     * @param name
+     * @param typeDesignation
+     */
+    @Transactional
+    private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
+
+        name.removeTypeDesignation(typeDesignation);
+        if (typeDesignation.getTypifiedNames().isEmpty()){
+            typeDesignation.removeType();
+            if (!typeDesignation.getRegistrations().isEmpty()){
+                for(Object reg: typeDesignation.getRegistrations()){
+                    if (reg instanceof Registration){
+                        ((Registration)reg).removeTypeDesignation(typeDesignation);
+                    }
+                }
+            }
+
+            typeDesignationDao.delete(typeDesignation);
+
+        }
+    }
+
+
+
+    /**
+     * @param name
+     * @param config
+     */
+    private void removeNameRelationshipsByDeleteConfig(TaxonName 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 ****************************************************************//
+
+
+    /**
+     * TODO candidate for harmonization
+     * new name findByName
+     */
+    @Override
+    @Deprecated
+    public List<TaxonName> getNamesByNameCache(String nameCache){
+        boolean includeAuthors = false;
+        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
+        return result;
+    }
+
+    /**
+     * TODO candidate for harmonization
+     * new name saveHomotypicalGroups
+     *
+     * findByTitle
+     */
+    @Override
+    public List<TaxonName> 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<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
+        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
+        return result;
+    }
+
+    @Override
+    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
+            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
+            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
+            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
+
+
+        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
+
+        List<TaxonNameParts> results;
+        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
+            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
+                    rank, excludedNamesUuids,
+                    pageSize, pageIndex, orderHints);
+        } else {
+            results = new ArrayList<>();
+        }
+
+        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
+            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
+
+        return findTaxonNameParts(
+                filter.uninomialQueryString(namePartQueryString),
+                filter.infraGenericEpithet(namePartQueryString),
+                filter.specificEpithet(namePartQueryString),
+                filter.infraspecificEpithet(namePartQueryString),
+                filter.getRank(),
+                filter.getExludedNamesUuids(),
+                pageSize, pageIndex, orderHints);
+    }
+
+    /**
+     * 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 getNomenclaturalStatus
+     */
+    @Override
+    public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
+        return nomStatusDao.list(limit, start);
+    }
+
+    @Override
+    public NomenclaturalStatus loadNomenclaturalStatus(UUID uuid,  List<String> propertyPaths){
+        return nomStatusDao.load(uuid, propertyPaths);
+    }
+
+    /**
+     * TODO candidate for harmonization
+     * new name getTypeDesignations
+     */
+    @Override
+    public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
+        return typeDesignationDao.getAllTypeDesignations(limit, start);
+    }
+
+    @Override
+    public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
+        return typeDesignationDao.load(id, propertyPaths);
+    }
+
+    @Override
+    public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
+        return typeDesignationDao.load(uuid, propertyPaths);
+    }
+
+    /**
+     * 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(INonViralName 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(TaxonName 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,
+            INonViralName 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, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.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, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.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, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.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<TaxonName>> findByNameFuzzySearch(
+            String name,
+            float accuracy,
+            List<Language> languages,
+            boolean highlightFragments,
+            List<String> propertyPaths,
+            int maxNoOfResults) throws IOException, LuceneParseException {
+
+        logger.info("Name to fuzzy search for : " + name);
+        // parse the input name
+        NonViralNameParserImpl parser = new NonViralNameParserImpl();
+        INonViralName nvn = parser.parseFullName(name);
+        if(name != null && !name.equals("") && nvn == null) {
+            throw new LuceneParseException("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<TaxonName>> 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 IOException, LuceneParseException {
+
+        logger.info("Name to fuzzy search for : " + name);
+        // parse the input name
+        NonViralNameParserImpl parser = new NonViralNameParserImpl();
+        INonViralName nvn = parser.parseFullName(name);
+        if(name != null && !name.equals("") && nvn == null) {
+            throw new LuceneParseException("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 IOException, LuceneParseException {
+
+        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 IOException, LuceneParseException {
+
+        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(TaxonName 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(TaxonName 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(TaxonName 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(TaxonName 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(TaxonName 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(TaxonName name, SpecimenTypeDesignationStatus status,
+            Integer pageSize, Integer pageNumber) {
+        return getTypeDesignations(name, status, pageSize, pageNumber, null);
+    }
+
+    @Override
+    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
+                Integer pageSize, Integer pageNumber, List<String> propertyPaths){
+        long numberOfResults = dao.countTypeDesignations(name, status);
+
+        List<TypeDesignationBase> results = new ArrayList<>();
+        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
+            results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
+        }
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
+    }
+
+    @Override
+    public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
+            Integer pageNumber, List<String> propertyPaths){
+        TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalReference.authorship"));
+        Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
+        List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
+        return result;
+    }
+
+    /**
+     * FIXME Candidate for harmonization
+     * rename search
+     */
+    @Override
+    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
+            List<String> propertyPaths) {
+        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
+
+        List<TaxonName> results = new ArrayList<>();
+        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<>(pageNumber, numberOfResults, pageSize, results);
+    }
+
+    @Override
+    public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
+        return dao.getUuidAndTitleCacheOfNames(limit, pattern);
+    }
+
+    @Override
+    public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
+            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
+         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
+
+         List<TaxonName> results = new ArrayList<>();
+         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<>(pageNumber, numberOfResults, pageSize, results);
+    }
+
+    @Override
+    public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
+        return homotypicalGroupDao.findByUuid(uuid);
+    }
+
+    @Override
+    @Transactional(readOnly = false)
+    public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
+        if (clazz == null){
+            clazz = TaxonName.class;
+        }
+        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
+    }
+
+
+    @Override
+    public List<TaggedText> getTaggedName(UUID uuid) {
+        TaxonName taxonName = dao.load(uuid);
+        List<TaggedText> taggedName = taxonName.getTaggedName();
+        return taggedName;
+    }
+
+
+    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
+        DeleteResult result = new DeleteResult();
+
+        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.isNonViral()){
+            INonViralName nvn = name;
+            Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
+            //Hibernate.initialize(parentHybridRelations);
+            if (! parentHybridRelations.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)){
+                NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
+
+                if (typeDesignation.getTypeName().equals(name)){
+                    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
+    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
+        TaxonName name = this.load(nameUUID);
+        return isDeletable(name, config);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
+        TaxonName 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();
+
+    }
+
+    @Override
+    public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
+        return typeDesignationDao.getTypeDesignationStatusInUse();
+    }
+
+    @Override
+    public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
+        List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
+        Map<String, TypeDesignationStatusFilter>  filterMap = new HashMap<>();
+        for(TypeDesignationStatusBase term : termList){
+            TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
+            String key = filter.getKey();
+            if(filterMap.containsKey(key)){
+                filterMap.get(key).addStatus(term);
+            } else {
+                filterMap.put(key, filter);
+            }
+        }
+        return filterMap.values();
+    }
+
+    @Override
+    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
+    }
+
+    @Override
+    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
+
+        List<S> records;
+        long resultSize = dao.count(clazz, restrictions);
+        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
+            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
+        } else {
+            records = new ArrayList<>();
+        }
+        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
+        return pager;
+    }
+
+
+
+}