-// $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;
+ }
+
+
+
+}