2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.api
.service
;
12 import java
.io
.IOException
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Collection
;
15 import java
.util
.HashMap
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
19 import java
.util
.Optional
;
21 import java
.util
.UUID
;
23 import org
.apache
.log4j
.Logger
;
24 import org
.apache
.lucene
.index
.Term
;
25 import org
.apache
.lucene
.sandbox
.queries
.FuzzyLikeThisQuery
;
26 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
27 import org
.apache
.lucene
.search
.BooleanQuery
;
28 import org
.apache
.lucene
.search
.BooleanQuery
.Builder
;
29 import org
.apache
.lucene
.search
.TopDocs
;
30 import org
.apache
.lucene
.search
.WildcardQuery
;
31 import org
.hibernate
.criterion
.Criterion
;
32 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
33 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
34 import org
.springframework
.stereotype
.Service
;
35 import org
.springframework
.transaction
.annotation
.Transactional
;
37 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
40 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
41 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
42 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
43 import eu
.etaxonomy
.cdm
.api
.service
.search
.DocumentSearchResult
;
44 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
45 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
46 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneParseException
;
47 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
51 import eu
.etaxonomy
.cdm
.api
.utility
.TaxonNamePartsFilter
;
52 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
53 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
54 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
55 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
56 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
57 import eu
.etaxonomy
.cdm
.model
.common
.ReferencedEntityBase
;
58 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
59 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
60 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
61 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
62 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
63 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationshipType
;
64 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
65 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
66 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationshipType
;
67 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignation
;
68 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
69 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
70 import eu
.etaxonomy
.cdm
.model
.name
.Registration
;
71 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
72 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
73 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
74 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
75 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
76 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
77 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
78 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IReferencedEntityDao
;
79 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ITermVocabularyDao
;
80 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.IHomotypicalGroupDao
;
81 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.INomenclaturalStatusDao
;
82 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
83 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITypeDesignationDao
;
84 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNameParts
;
85 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
86 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
87 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
88 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
89 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
90 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
94 @Transactional(readOnly
= true)
95 public class NameServiceImpl
extends IdentifiableServiceBase
<TaxonName
,ITaxonNameDao
> implements INameService
{
96 static private final Logger logger
= Logger
.getLogger(NameServiceImpl
.class);
99 protected ITermVocabularyDao vocabularyDao
;
101 protected IOrderedTermVocabularyDao orderedVocabularyDao
;
103 @Qualifier("refEntDao")
104 protected IReferencedEntityDao
<ReferencedEntityBase
> referencedEntityDao
;
106 private INomenclaturalStatusDao nomStatusDao
;
108 private ITypeDesignationDao typeDesignationDao
;
110 private IHomotypicalGroupDao homotypicalGroupDao
;
112 private ICdmGenericDao genericDao
;
114 private ILuceneIndexToolProvider luceneIndexToolProvider
;
119 public NameServiceImpl(){
120 if (logger
.isDebugEnabled()) { logger
.debug("Load NameService Bean"); }
123 //********************* METHODS ****************************************************************//
126 * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
129 @Transactional(readOnly
= false)
130 public DeleteResult
delete(UUID nameUUID
){
131 NameDeletionConfigurator config
= new NameDeletionConfigurator();
132 DeleteResult result
= delete(nameUUID
, config
);
137 public DeleteResult
delete(TaxonName name
){
138 return delete(name
.getUuid());
142 @Transactional(readOnly
= false)
143 public DeleteResult
delete(TaxonName name
, NameDeletionConfigurator config
) {
144 DeleteResult result
= new DeleteResult();
153 result
= this.isDeletable(name
, config
);
155 result
.addException(e
);
160 //remove references to this name
161 removeNameRelationshipsByDeleteConfig(name
, config
);
163 //remove name from homotypical group
164 HomotypicalGroup homotypicalGroup
= name
.getHomotypicalGroup();
165 if (homotypicalGroup
!= null){
166 homotypicalGroup
.removeTypifiedName(name
, false);
169 //all type designation relationships are removed as they belong to the name
170 deleteTypeDesignation(name
, null);
171 // //type designations
172 // if (! name.getTypeDesignations().isEmpty()){
173 // String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
174 // throw new ReferrencedObjectUndeletableException(message);
179 UUID nameUuid
= dao
.delete(name
);
180 result
.addDeletedObject(name
);
182 result
.addException(e
);
192 @Transactional(readOnly
= false)
193 public DeleteResult
delete(UUID nameUUID
, NameDeletionConfigurator config
) {
195 TaxonName name
= dao
.load(nameUUID
);
196 return delete(name
, config
);
201 public DeleteResult
deleteTypeDesignation(TaxonName name
, TypeDesignationBase typeDesignation
){
202 if(typeDesignation
!=null && typeDesignation
.getId()!=0){
203 typeDesignation
= HibernateProxyHelper
.deproxy(referencedEntityDao
.load(typeDesignation
.getUuid()), TypeDesignationBase
.class);
206 DeleteResult result
= new DeleteResult();
207 if (name
== null && typeDesignation
== null){
210 }else if (name
!= null && typeDesignation
!= null){
211 removeSingleDesignation(name
, typeDesignation
);
212 }else if (name
!= null){
213 Set
<TypeDesignationBase
> designationSet
= new HashSet
<TypeDesignationBase
>(name
.getTypeDesignations());
214 for (Object o
: designationSet
){
215 TypeDesignationBase desig
= CdmBase
.deproxy(o
, TypeDesignationBase
.class);
216 removeSingleDesignation(name
, desig
);
218 }else if (typeDesignation
!= null){
219 Set
<TaxonName
> nameSet
= new HashSet
<TaxonName
>(typeDesignation
.getTypifiedNames());
220 for (Object o
: nameSet
){
221 TaxonName singleName
= CdmBase
.deproxy(o
, TaxonName
.class);
222 removeSingleDesignation(singleName
, typeDesignation
);
225 result
.addDeletedObject(typeDesignation
);
226 result
.addUpdatedObject(name
);
232 @Transactional(readOnly
= false)
233 public DeleteResult
deleteTypeDesignation(UUID nameUuid
, UUID typeDesignationUuid
){
234 TaxonName nameBase
= load(nameUuid
);
235 TypeDesignationBase typeDesignation
= HibernateProxyHelper
.deproxy(referencedEntityDao
.load(typeDesignationUuid
), TypeDesignationBase
.class);
236 return deleteTypeDesignation(nameBase
, typeDesignation
);
241 * @param typeDesignation
244 private void removeSingleDesignation(TaxonName name
, TypeDesignationBase typeDesignation
) {
245 name
.removeTypeDesignation(typeDesignation
);
246 if (typeDesignation
.getTypifiedNames().isEmpty()){
247 typeDesignation
.removeType();
248 if (!typeDesignation
.getRegistrations().isEmpty()){
249 for(Object reg
: typeDesignation
.getRegistrations()){
250 if (reg
instanceof Registration
){
251 ((Registration
)reg
).removeTypeDesignation(typeDesignation
);
255 typeDesignationDao
.delete(typeDesignation
);
266 private void removeNameRelationshipsByDeleteConfig(TaxonName name
, NameDeletionConfigurator config
) {
268 if (config
.isRemoveAllNameRelationships()){
269 Set
<NameRelationship
> rels
= getModifiableSet(name
.getNameRelations());
270 for (NameRelationship rel
: rels
){
271 name
.removeNameRelationship(rel
);
274 //relations to this name
275 Set
<NameRelationship
> rels
= getModifiableSet(name
.getRelationsToThisName());
276 for (NameRelationship rel
: rels
){
277 if (config
.isIgnoreHasBasionym() && NameRelationshipType
.BASIONYM().equals(rel
.getType() )){
278 name
.removeNameRelationship(rel
);
279 }else if (config
.isIgnoreHasReplacedSynonym() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
280 name
.removeNameRelationship(rel
);
283 //relations from this name
284 rels
= getModifiableSet(name
.getRelationsFromThisName());
285 for (NameRelationship rel
: rels
){
286 if (config
.isIgnoreIsBasionymFor() && NameRelationshipType
.BASIONYM().equals(rel
.getType()) ){
287 name
.removeNameRelationship(rel
);
288 }else if (config
.isIgnoreIsReplacedSynonymFor() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
289 name
.removeNameRelationship(rel
);
294 } catch (Exception e
) {
295 throw new RuntimeException(e
);
303 private Set
<NameRelationship
> getModifiableSet(Set
<NameRelationship
> relations
) {
304 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
305 for (NameRelationship rel
: relations
){
311 //********************* METHODS ****************************************************************//
315 * TODO candidate for harmonization
316 * new name findByName
320 public List
<TaxonName
> getNamesByNameCache(String nameCache
){
321 boolean includeAuthors
= false;
322 List result
= dao
.findByName(includeAuthors
, nameCache
, MatchMode
.EXACT
, null, null, null, null);
328 * TODO candidate for harmonization
329 * new name saveHomotypicalGroups
334 public List
<TaxonName
> findNamesByTitleCache(String titleCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
335 List result
= dao
.findByTitle(titleCache
, matchMode
, null, null, null, propertyPaths
);
340 * TODO candidate for harmonization
341 * new name saveHomotypicalGroups
346 public List
<TaxonName
> findNamesByNameCache(String nameCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
347 List result
= dao
.findByName(false, nameCache
, matchMode
, null, null, null , propertyPaths
);
352 public Pager
<TaxonNameParts
> findTaxonNameParts(Optional
<String
> genusOrUninomial
,
353 Optional
<String
> infraGenericEpithet
, Optional
<String
> specificEpithet
,
354 Optional
<String
> infraSpecificEpithet
, Rank rank
, Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
357 long count
= dao
.countTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraGenericEpithet
, rank
);
359 List
<TaxonNameParts
> results
;
360 if(AbstractPagerImpl
.hasResultsInRange(count
, pageIndex
, pageSize
)){
361 results
= dao
.findTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraSpecificEpithet
,
363 pageSize
, pageIndex
, orderHints
);
365 results
= new ArrayList
<>();
368 return new DefaultPagerImpl
<TaxonNameParts
>(pageIndex
, count
, pageSize
, results
);
375 public Pager
<TaxonNameParts
> findTaxonNameParts(TaxonNamePartsFilter filter
, String namePartQueryString
,
376 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
378 return findTaxonNameParts(
379 filter
.uninomialQueryString(namePartQueryString
),
380 filter
.infraGenericEpithet(namePartQueryString
),
381 filter
.specificEpithet(namePartQueryString
),
382 filter
.infraspecificEpithet(namePartQueryString
),
384 pageSize
, pageIndex
, orderHints
);
388 * TODO candidate for harmonization
391 public List
getNamesByName(String name
, CdmBase sessionObject
){
392 return super.findCdmObjectsByTitle(name
, sessionObject
);
396 * TODO candidate for harmonization
397 * new name saveHomotypicalGroups
400 @Transactional(readOnly
= false)
401 public Map
<UUID
, HomotypicalGroup
> saveAllHomotypicalGroups(Collection
<HomotypicalGroup
> homotypicalGroups
){
402 return homotypicalGroupDao
.saveAll(homotypicalGroups
);
406 * TODO candidate for harmonization
407 * new name saveTypeDesignations
410 @Transactional(readOnly
= false)
411 public Map
<UUID
, TypeDesignationBase
> saveTypeDesignationAll(Collection
<TypeDesignationBase
> typeDesignationCollection
){
412 return typeDesignationDao
.saveAll(typeDesignationCollection
);
416 * TODO candidate for harmonization
417 * new name saveReferencedEntities
420 @Transactional(readOnly
= false)
421 public Map
<UUID
, ReferencedEntityBase
> saveReferencedEntitiesAll(Collection
<ReferencedEntityBase
> referencedEntityCollection
){
422 return referencedEntityDao
.saveAll(referencedEntityCollection
);
426 * TODO candidate for harmonization
427 * new name getNomenclaturalStatus
430 public List
<NomenclaturalStatus
> getAllNomenclaturalStatus(int limit
, int start
){
431 return nomStatusDao
.list(limit
, start
);
435 * TODO candidate for harmonization
436 * new name getTypeDesignations
439 public List
<TypeDesignationBase
> getAllTypeDesignations(int limit
, int start
){
440 return typeDesignationDao
.getAllTypeDesignations(limit
, start
);
444 public TypeDesignationBase
loadTypeDesignation(int id
, List
<String
> propertyPaths
){
445 return typeDesignationDao
.load(id
, propertyPaths
);
449 public TypeDesignationBase
loadTypeDesignation(UUID uuid
, List
<String
> propertyPaths
){
450 return typeDesignationDao
.load(uuid
, propertyPaths
);
454 * FIXME Candidate for harmonization
455 * homotypicalGroupService.list
458 public List
<HomotypicalGroup
> getAllHomotypicalGroups(int limit
, int start
){
459 return homotypicalGroupDao
.list(limit
, start
);
463 * FIXME Candidate for harmonization
468 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
469 return dao
.getAllRelationships(limit
, start
);
476 protected void setDao(ITaxonNameDao dao
) {
481 public Pager
<HybridRelationship
> getHybridNames(INonViralName name
, HybridRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
482 Integer numberOfResults
= dao
.countHybridNames(name
, type
);
484 List
<HybridRelationship
> results
= new ArrayList
<HybridRelationship
>();
485 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
486 results
= dao
.getHybridNames(name
, type
, pageSize
, pageNumber
,orderHints
,propertyPaths
);
489 return new DefaultPagerImpl
<HybridRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
493 public List
<NameRelationship
> listNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
494 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
496 Integer numberOfResults
= dao
.countNameRelationships(name
, direction
, type
);
498 List
<NameRelationship
> results
= new ArrayList
<NameRelationship
>();
499 if (AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
500 results
= dao
.getNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
506 protected LuceneSearch
prepareFindByFuzzyNameSearch(Class
<?
extends CdmBase
> clazz
,
510 List
<Language
> languages
,
511 boolean highlightFragments
) {
512 String similarity
= Float
.toString(accuracy
);
513 String searchSuffix
= "~" + similarity
;
516 Builder finalQueryBuilder
= new Builder();
517 finalQueryBuilder
.setDisableCoord(false);
518 Builder textQueryBuilder
= new Builder();
519 textQueryBuilder
.setDisableCoord(false);
521 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
522 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
524 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
525 // luceneSearch.setSortFields(sortFields);
527 // ---- search criteria
528 luceneSearch
.setCdmTypRestriction(clazz
);
530 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
531 if(nvn
.getGenusOrUninomial() != null && !nvn
.getGenusOrUninomial().equals("")) {
532 fltq
.addTerms(nvn
.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy
, 3);
534 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
535 textQueryBuilder
.add(queryFactory
.newTermQuery("genusOrUninomial", "_null_", false), Occur
.MUST
);
538 if(nvn
.getInfraGenericEpithet() != null && !nvn
.getInfraGenericEpithet().equals("")){
539 fltq
.addTerms(nvn
.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy
, 3);
541 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
542 textQueryBuilder
.add(queryFactory
.newTermQuery("infraGenericEpithet", "_null_", false), Occur
.MUST
);
545 if(nvn
.getSpecificEpithet() != null && !nvn
.getSpecificEpithet().equals("")){
546 fltq
.addTerms(nvn
.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy
, 3);
548 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
549 textQueryBuilder
.add(queryFactory
.newTermQuery("specificEpithet", "_null_", false), Occur
.MUST
);
552 if(nvn
.getInfraSpecificEpithet() != null && !nvn
.getInfraSpecificEpithet().equals("")){
553 fltq
.addTerms(nvn
.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy
, 3);
555 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
556 textQueryBuilder
.add(queryFactory
.newTermQuery("infraSpecificEpithet", "_null_", false), Occur
.MUST
);
559 if(nvn
.getAuthorshipCache() != null && !nvn
.getAuthorshipCache().equals("")){
560 fltq
.addTerms(nvn
.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy
, 3);
562 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
565 textQueryBuilder
.add(fltq
, Occur
.MUST
);
567 BooleanQuery textQuery
= textQueryBuilder
.build();
568 finalQueryBuilder
.add(textQuery
, Occur
.MUST
);
570 luceneSearch
.setQuery(finalQueryBuilder
.build());
572 if(highlightFragments
){
573 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
578 protected LuceneSearch
prepareFindByFuzzyNameCacheSearch(Class
<?
extends CdmBase
> clazz
,
582 List
<Language
> languages
,
583 boolean highlightFragments
) {
585 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
586 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
588 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
589 // luceneSearch.setSortFields(sortFields);
591 // ---- search criteria
592 luceneSearch
.setCdmTypRestriction(clazz
);
593 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
595 fltq
.addTerms(name
, "nameCache", accuracy
, 3);
597 BooleanQuery finalQuery
= new BooleanQuery(false);
599 finalQuery
.add(fltq
, Occur
.MUST
);
601 luceneSearch
.setQuery(finalQuery
);
603 if(highlightFragments
){
604 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
609 protected LuceneSearch
prepareFindByExactNameSearch(Class
<?
extends CdmBase
> clazz
,
612 List
<Language
> languages
,
613 boolean highlightFragments
) {
614 Builder textQueryBuilder
= new Builder();
616 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
617 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
619 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
620 // luceneSearch.setSortFields(sortFields);
622 // ---- search criteria
623 luceneSearch
.setCdmTypRestriction(clazz
);
625 if(name
!= null && !name
.equals("")) {
627 textQueryBuilder
.add(new WildcardQuery(new Term("nameCache", name
+ "*")), Occur
.MUST
);
629 textQueryBuilder
.add(queryFactory
.newTermQuery("nameCache", name
, false), Occur
.MUST
);
633 luceneSearch
.setQuery(textQueryBuilder
.build());
635 if(highlightFragments
){
636 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
642 public List
<SearchResult
<TaxonName
>> findByNameFuzzySearch(
645 List
<Language
> languages
,
646 boolean highlightFragments
,
647 List
<String
> propertyPaths
,
648 int maxNoOfResults
) throws IOException
, LuceneParseException
{
650 logger
.info("Name to fuzzy search for : " + name
);
651 // parse the input name
652 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
653 INonViralName nvn
= parser
.parseFullName(name
);
654 if(name
!= null && !name
.equals("") && nvn
== null) {
655 throw new LuceneParseException("Could not parse name " + name
);
657 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
659 // --- execute search
660 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
663 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
664 idFieldMap
.put(CdmBaseType
.NONVIRALNAME
, "id");
666 // --- initialize taxa, highlight matches ....
667 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
669 @SuppressWarnings("rawtypes")
670 List
<SearchResult
<TaxonName
>> searchResults
= searchResultBuilder
.createResultSet(
671 topDocs
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
673 return searchResults
;
678 public List
<DocumentSearchResult
> findByNameFuzzySearch(
681 List
<Language
> languages
,
682 boolean highlightFragments
,
683 int maxNoOfResults
) throws IOException
, LuceneParseException
{
685 logger
.info("Name to fuzzy search for : " + name
);
686 // parse the input name
687 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
688 INonViralName nvn
= parser
.parseFullName(name
);
689 if(name
!= null && !name
.equals("") && nvn
== null) {
690 throw new LuceneParseException("Could not parse name " + name
);
692 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
694 // --- execute search
695 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
697 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
699 // --- initialize taxa, highlight matches ....
700 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
702 @SuppressWarnings("rawtypes")
703 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
705 return searchResults
;
709 public List
<DocumentSearchResult
> findByFuzzyNameCacheSearch(
712 List
<Language
> languages
,
713 boolean highlightFragments
,
714 int maxNoOfResults
) throws IOException
, LuceneParseException
{
716 logger
.info("Name to fuzzy search for : " + name
);
718 LuceneSearch luceneSearch
= prepareFindByFuzzyNameCacheSearch(null, name
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
720 // --- execute search
721 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
722 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
724 // --- initialize taxa, highlight matches ....
725 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
727 @SuppressWarnings("rawtypes")
728 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
730 return searchResults
;
734 public List
<DocumentSearchResult
> findByNameExactSearch(
737 List
<Language
> languages
,
738 boolean highlightFragments
,
739 int maxNoOfResults
) throws IOException
, LuceneParseException
{
741 logger
.info("Name to exact search for : " + name
);
743 LuceneSearch luceneSearch
= prepareFindByExactNameSearch(null, name
, wildcard
, languages
, highlightFragments
);
745 // --- execute search
748 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
750 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
752 // --- initialize taxa, highlight matches ....
753 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
755 @SuppressWarnings("rawtypes")
756 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
758 return searchResults
;
762 public Pager
<NameRelationship
> pageNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
763 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
764 List
<NameRelationship
> results
= listNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
765 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
769 public List
<NameRelationship
> listFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
770 return listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
774 public Pager
<NameRelationship
> pageFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
775 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
776 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
780 public List
<NameRelationship
> listToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
781 return listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
785 public Pager
<NameRelationship
> pageToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
786 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
787 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
791 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
792 Integer pageSize
, Integer pageNumber
) {
793 return getTypeDesignations(name
, status
, pageSize
, pageNumber
, null);
797 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
798 Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
){
799 long numberOfResults
= dao
.countTypeDesignations(name
, status
);
801 List
<TypeDesignationBase
> results
= new ArrayList
<>();
802 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
, pageNumber
, pageSize
)) {
803 results
= dao
.getTypeDesignations(name
, null, status
, pageSize
, pageNumber
, propertyPaths
);
806 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
810 * FIXME Candidate for harmonization
814 public Pager
<TaxonName
> searchNames(String uninomial
,String infraGenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
815 List
<String
> propertyPaths
) {
816 long numberOfResults
= dao
.countNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
818 List
<TaxonName
> results
= new ArrayList
<>();
819 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
820 results
= dao
.searchNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
823 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
827 public List
<UuidAndTitleCache
> getUuidAndTitleCacheOfNames(Integer limit
, String pattern
) {
828 return dao
.getUuidAndTitleCacheOfNames(limit
, pattern
);
832 public Pager
<TaxonName
> findByName(Class
<?
extends TaxonName
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criteria
, Integer pageSize
,Integer pageNumber
, List
<OrderHint
> orderHints
,List
<String
> propertyPaths
) {
833 Long numberOfResults
= dao
.countByName(clazz
, queryString
, matchmode
, criteria
);
835 List
<TaxonName
> results
= new ArrayList
<>();
836 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
837 results
= dao
.findByName(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
840 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
844 public HomotypicalGroup
findHomotypicalGroup(UUID uuid
) {
845 return homotypicalGroupDao
.findByUuid(uuid
);
849 @Transactional(readOnly
= false)
850 public void updateTitleCache(Class
<?
extends TaxonName
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonName
> cacheStrategy
, IProgressMonitor monitor
) {
852 clazz
= TaxonName
.class;
854 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
859 protected void setOtherCachesNull(TaxonName name
) {
860 if (! name
.isProtectedNameCache()){
861 name
.setNameCache(null, false);
863 if (! name
.isProtectedAuthorshipCache()){
864 name
.setAuthorshipCache(null, false);
866 if (! name
.isProtectedFullTitleCache()){
867 name
.setFullTitleCache(null, false);
873 public List
<TaggedText
> getTaggedName(UUID uuid
) {
874 TaxonName taxonName
= dao
.load(uuid
);
875 List
<TaggedText
> taggedName
= taxonName
.getTaggedName();
880 public DeleteResult
isDeletable(TaxonName name
, DeleteConfiguratorBase config
){
881 DeleteResult result
= new DeleteResult();
883 NameDeletionConfigurator nameConfig
= null;
884 if (config
instanceof NameDeletionConfigurator
){
885 nameConfig
= (NameDeletionConfigurator
) config
;
887 result
.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
892 if (!name
.getNameRelations().isEmpty() && !nameConfig
.isRemoveAllNameRelationships()){
893 HomotypicalGroup homotypicalGroup
= HibernateProxyHelper
.deproxy(name
.getHomotypicalGroup(), HomotypicalGroup
.class);
895 if (!nameConfig
.isIgnoreIsBasionymFor() && homotypicalGroup
.getBasionyms().contains(name
)){
896 result
.addException(new Exception( "Name can't be deleted as it is a basionym."));
899 if (!nameConfig
.isIgnoreHasBasionym() && (name
.getBasionyms().size()>0)){
900 result
.addException(new Exception( "Name can't be deleted as it has a basionym."));
903 Set
<NameRelationship
> relationships
= name
.getNameRelations();
904 for (NameRelationship rel
: relationships
){
905 if (!rel
.getType().equals(NameRelationshipType
.BASIONYM())){
906 result
.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
914 if (! name
.getTaxonBases().isEmpty()){
915 result
.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
919 //hybrid relationships
920 if (name
.isNonViral()){
921 INonViralName nvn
= name
;
922 Set
<HybridRelationship
> parentHybridRelations
= nvn
.getHybridParentRelations();
923 //Hibernate.initialize(parentHybridRelations);
924 if (! parentHybridRelations
.isEmpty()){
925 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."));
929 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjectsForDeletion(name
);
930 for (CdmBase referencingObject
: referencingObjects
){
931 //DerivedUnit?.storedUnder
932 if (referencingObject
.isInstanceOf(DerivedUnit
.class)){
933 String message
= "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
934 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnit
.class).getTitleCache());
935 result
.addException(new ReferencedObjectUndeletableException(message
));
936 result
.addRelatedObject(referencingObject
);
939 //DescriptionElementSource#nameUsedInSource
940 else if (referencingObject
.isInstanceOf(DescriptionElementSource
.class)){
941 String message
= "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
942 result
.addException(new ReferencedObjectUndeletableException(message
));
943 result
.addRelatedObject(referencingObject
);
946 //NameTypeDesignation#typeName
947 else if (referencingObject
.isInstanceOf(NameTypeDesignation
.class)){
948 NameTypeDesignation typeDesignation
= HibernateProxyHelper
.deproxy(referencingObject
, NameTypeDesignation
.class);
950 if (typeDesignation
.getTypeName().equals(name
)){
951 String message
= "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
952 result
.addException(new ReferencedObjectUndeletableException(message
));
953 result
.addRelatedObject(referencingObject
);
957 //DeterminationEvent#taxonName
958 else if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
959 String message
= "Name can't be deleted as it is used as a determination event";
960 result
.addException(new ReferencedObjectUndeletableException(message
));
961 result
.addRelatedObject(referencingObject
);
966 //TODO inline references
969 if (!nameConfig
.isIgnoreIsReplacedSynonymFor() && name
.isReplacedSynonym()){
970 String message
= "Name can't be deleted as it is a replaced synonym.";
971 result
.addException(new Exception(message
));
974 if (!nameConfig
.isIgnoreHasReplacedSynonym() && (name
.getReplacedSynonyms().size()>0)){
975 String message
= "Name can't be deleted as it has a replaced synonym.";
976 result
.addException(new Exception(message
));
985 public DeleteResult
isDeletable(UUID nameUUID
, DeleteConfiguratorBase config
){
986 TaxonName name
= this.load(nameUUID
);
987 return isDeletable(name
, config
);
991 @Transactional(readOnly
= true)
992 public UpdateResult
setAsGroupsBasionym(UUID nameUuid
) {
993 TaxonName name
= dao
.load(nameUuid
);
994 UpdateResult result
= new UpdateResult();
995 name
.setAsGroupsBasionym();
996 result
.addUpdatedObject(name
);
1002 public List
<HashMap
<String
,String
>> getNameRecords(){
1004 return dao
.getNameRecords();