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
.Arrays
;
15 import java
.util
.Collection
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
20 import java
.util
.Optional
;
22 import java
.util
.UUID
;
24 import org
.apache
.log4j
.Logger
;
25 import org
.apache
.lucene
.index
.Term
;
26 import org
.apache
.lucene
.sandbox
.queries
.FuzzyLikeThisQuery
;
27 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
28 import org
.apache
.lucene
.search
.BooleanQuery
;
29 import org
.apache
.lucene
.search
.BooleanQuery
.Builder
;
30 import org
.apache
.lucene
.search
.TopDocs
;
31 import org
.apache
.lucene
.search
.WildcardQuery
;
32 import org
.hibernate
.criterion
.Criterion
;
33 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
34 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
35 import org
.springframework
.stereotype
.Service
;
36 import org
.springframework
.transaction
.annotation
.Transactional
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
40 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TypeDesignationStatusFilter
;
41 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
42 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
45 import eu
.etaxonomy
.cdm
.api
.service
.search
.DocumentSearchResult
;
46 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
47 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneParseException
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
53 import eu
.etaxonomy
.cdm
.api
.utility
.TaxonNamePartsFilter
;
54 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
55 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
56 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
57 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
58 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
59 import eu
.etaxonomy
.cdm
.model
.common
.ReferencedEntityBase
;
60 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
61 import eu
.etaxonomy
.cdm
.model
.common
.SourcedEntityBase
;
62 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
63 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
64 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
65 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationshipType
;
66 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
67 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
68 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationshipType
;
69 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignation
;
70 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
71 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
72 import eu
.etaxonomy
.cdm
.model
.name
.Registration
;
73 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
74 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
75 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
76 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
77 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationStatusBase
;
78 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
79 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
80 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
81 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
82 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
83 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
84 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IReferencedEntityDao
;
85 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ISourcedEntityDao
;
86 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
87 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
88 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.IHomotypicalGroupDao
;
89 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.INomenclaturalStatusDao
;
90 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
91 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITypeDesignationDao
;
92 import eu
.etaxonomy
.cdm
.persistence
.dao
.term
.IOrderedTermVocabularyDao
;
93 import eu
.etaxonomy
.cdm
.persistence
.dao
.term
.ITermVocabularyDao
;
94 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNameParts
;
95 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
96 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
97 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
98 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
99 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
100 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
104 @Transactional(readOnly
= true)
105 public class NameServiceImpl
106 extends IdentifiableServiceBase
<TaxonName
,ITaxonNameDao
>
107 implements INameService
{
108 static private final Logger logger
= Logger
.getLogger(NameServiceImpl
.class);
111 protected ITermVocabularyDao vocabularyDao
;
113 protected IOrderedTermVocabularyDao orderedVocabularyDao
;
115 protected IOccurrenceService occurrenceService
;
117 protected ICollectionService collectionService
;
119 @Qualifier("refEntDao")
120 protected IReferencedEntityDao
<ReferencedEntityBase
> referencedEntityDao
;
122 @Qualifier("sourcedEntityDao")
123 protected ISourcedEntityDao
<SourcedEntityBase
<?
>> sourcedEntityDao
;
125 private INomenclaturalStatusDao nomStatusDao
;
127 private ITypeDesignationDao typeDesignationDao
;
129 private IHomotypicalGroupDao homotypicalGroupDao
;
131 private ICdmGenericDao genericDao
;
133 private ILuceneIndexToolProvider luceneIndexToolProvider
;
135 // @Qualifier("defaultBeanInitializer")
136 protected IBeanInitializer defaultBeanInitializer
;
141 public NameServiceImpl(){}
143 //********************* METHODS ****************************************************************//
146 @Transactional(readOnly
= false)
147 public DeleteResult
delete(UUID nameUUID
){
148 NameDeletionConfigurator config
= new NameDeletionConfigurator();
149 DeleteResult result
= delete(nameUUID
, config
);
154 public DeleteResult
delete(TaxonName name
){
155 return delete(name
.getUuid());
159 @Transactional(readOnly
= false)
160 public DeleteResult
delete(TaxonName name
, NameDeletionConfigurator config
) {
161 DeleteResult result
= new DeleteResult();
169 result
= this.isDeletable(name
, config
);
171 result
.addException(e
);
176 //remove references to this name
177 removeNameRelationshipsByDeleteConfig(name
, config
);
179 //remove name from homotypical group
180 HomotypicalGroup homotypicalGroup
= name
.getHomotypicalGroup();
181 if (homotypicalGroup
!= null){
182 homotypicalGroup
.removeTypifiedName(name
, false);
185 //all type designation relationships are removed as they belong to the name
186 deleteTypeDesignation(name
, null);
187 // //type designations
188 // if (! name.getTypeDesignations().isEmpty()){
189 // String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
190 // throw new ReferrencedObjectUndeletableException(message);
195 result
.addDeletedObject(name
);
197 result
.addException(e
);
207 @Transactional(readOnly
= false)
208 public DeleteResult
delete(UUID nameUUID
, NameDeletionConfigurator config
) {
210 TaxonName name
= dao
.load(nameUUID
);
211 return delete(name
, config
);
215 @Transactional(readOnly
= false)
216 public UpdateResult
cloneTypeDesignation(UUID nameUuid
, SpecimenTypeDesignation baseDesignation
,
217 String accessionNumber
, String barcode
, String catalogNumber
,
218 UUID collectionUuid
, SpecimenTypeDesignationStatus typeStatus
){
219 UpdateResult result
= new UpdateResult();
221 DerivedUnit baseSpecimen
= HibernateProxyHelper
.deproxy(occurrenceService
.load(baseDesignation
.getTypeSpecimen().getUuid(), Arrays
.asList("collection")), DerivedUnit
.class);
222 DerivedUnit duplicate
= DerivedUnit
.NewInstance(baseSpecimen
.getRecordBasis());
223 DerivationEvent derivedFrom
= baseSpecimen
.getDerivedFrom();
224 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(baseSpecimen
.getUuid(), null);
225 if(fieldUnits
.size()!=1){
226 result
.addException(new Exception("More than one or no field unit found for specimen"));
230 for (SpecimenOrObservationBase original
: derivedFrom
.getOriginals()) {
231 DerivationEvent
.NewSimpleInstance(original
, duplicate
, derivedFrom
.getType());
233 duplicate
.setAccessionNumber(accessionNumber
);
234 duplicate
.setBarcode(barcode
);
235 duplicate
.setCatalogNumber(catalogNumber
);
236 duplicate
.setCollection(collectionService
.load(collectionUuid
));
237 SpecimenTypeDesignation typeDesignation
= SpecimenTypeDesignation
.NewInstance();
238 typeDesignation
.setTypeSpecimen(duplicate
);
239 typeDesignation
.setTypeStatus(typeStatus
);
241 TaxonName name
= load(nameUuid
);
242 name
.getTypeDesignations().add(typeDesignation
);
244 result
.setCdmEntity(typeDesignation
);
245 result
.addUpdatedObject(name
);
251 public DeleteResult
deleteTypeDesignation(TaxonName name
, TypeDesignationBase
<?
> typeDesignation
){
252 if(typeDesignation
!= null && typeDesignation
.isPersited()){
253 typeDesignation
= HibernateProxyHelper
.deproxy(sourcedEntityDao
.load(typeDesignation
.getUuid()), TypeDesignationBase
.class);
256 DeleteResult result
= new DeleteResult();
257 if (name
== null && typeDesignation
== null){
260 }else if (name
!= null && typeDesignation
!= null){
261 removeSingleDesignation(name
, typeDesignation
);
262 }else if (name
!= null){
263 @SuppressWarnings("rawtypes")
264 Set
<TypeDesignationBase
<?
>> designationSet
= new HashSet(name
.getTypeDesignations());
265 for (TypeDesignationBase
<?
> desig
: designationSet
){
266 desig
= CdmBase
.deproxy(desig
);
267 removeSingleDesignation(name
, desig
);
269 }else if (typeDesignation
!= null){
270 @SuppressWarnings("unchecked")
271 Set
<TaxonName
> nameSet
= new HashSet(typeDesignation
.getTypifiedNames());
272 for (TaxonName singleName
: nameSet
){
273 singleName
= CdmBase
.deproxy(singleName
);
274 removeSingleDesignation(singleName
, typeDesignation
);
277 result
.addDeletedObject(typeDesignation
);
278 result
.addUpdatedObject(name
);
284 @Transactional(readOnly
= false)
285 public DeleteResult
deleteTypeDesignation(UUID nameUuid
, UUID typeDesignationUuid
){
286 TaxonName nameBase
= load(nameUuid
);
287 TypeDesignationBase
<?
> typeDesignation
= HibernateProxyHelper
.deproxy(sourcedEntityDao
.load(typeDesignationUuid
), TypeDesignationBase
.class);
288 return deleteTypeDesignation(nameBase
, typeDesignation
);
293 * @param typeDesignation
296 private void removeSingleDesignation(TaxonName name
, TypeDesignationBase
<?
> typeDesignation
) {
298 name
.removeTypeDesignation(typeDesignation
);
299 if (typeDesignation
.getTypifiedNames().isEmpty()){
300 typeDesignation
.removeType();
301 if (!typeDesignation
.getRegistrations().isEmpty()){
302 for(Object reg
: typeDesignation
.getRegistrations()){
303 if (reg
instanceof Registration
){
304 ((Registration
)reg
).removeTypeDesignation(typeDesignation
);
309 typeDesignationDao
.delete(typeDesignation
);
320 private void removeNameRelationshipsByDeleteConfig(TaxonName name
, NameDeletionConfigurator config
) {
322 if (config
.isRemoveAllNameRelationships()){
323 Set
<NameRelationship
> rels
= getModifiableSet(name
.getNameRelations());
324 for (NameRelationship rel
: rels
){
325 name
.removeNameRelationship(rel
);
328 //relations to this name
329 Set
<NameRelationship
> rels
= getModifiableSet(name
.getRelationsToThisName());
330 for (NameRelationship rel
: rels
){
331 if (config
.isIgnoreHasBasionym() && NameRelationshipType
.BASIONYM().equals(rel
.getType() )){
332 name
.removeNameRelationship(rel
);
333 }else if (config
.isIgnoreHasReplacedSynonym() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
334 name
.removeNameRelationship(rel
);
337 //relations from this name
338 rels
= getModifiableSet(name
.getRelationsFromThisName());
339 for (NameRelationship rel
: rels
){
340 if (config
.isIgnoreIsBasionymFor() && NameRelationshipType
.BASIONYM().equals(rel
.getType()) ){
341 name
.removeNameRelationship(rel
);
342 }else if (config
.isIgnoreIsReplacedSynonymFor() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
343 name
.removeNameRelationship(rel
);
348 } catch (Exception e
) {
349 throw new RuntimeException(e
);
357 private Set
<NameRelationship
> getModifiableSet(Set
<NameRelationship
> relations
) {
358 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
359 for (NameRelationship rel
: relations
){
365 //********************* METHODS ****************************************************************//
369 * TODO candidate for harmonization
370 * new name findByName
374 public List
<TaxonName
> getNamesByNameCache(String nameCache
){
375 boolean includeAuthors
= false;
376 List result
= dao
.findByName(includeAuthors
, nameCache
, MatchMode
.EXACT
, null, null, null, null);
381 * TODO candidate for harmonization
382 * new name saveHomotypicalGroups
388 public List
<TaxonName
> findNamesByTitleCache(String titleCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
389 List result
= dao
.findByTitle(titleCache
, matchMode
, null, null, null, propertyPaths
);
394 * TODO candidate for harmonization
395 * new name saveHomotypicalGroups
401 public List
<TaxonName
> findNamesByNameCache(String nameCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
402 List
<TaxonName
> result
= dao
.findByName(false, nameCache
, matchMode
, null, null, null , propertyPaths
);
407 public Pager
<TaxonNameParts
> findTaxonNameParts(Optional
<String
> genusOrUninomial
,
408 Optional
<String
> infraGenericEpithet
, Optional
<String
> specificEpithet
,
409 Optional
<String
> infraSpecificEpithet
, Rank rank
, Set
<UUID
> excludedNamesUuids
,
410 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
413 long count
= dao
.countTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraGenericEpithet
, rank
, excludedNamesUuids
);
415 List
<TaxonNameParts
> results
;
416 if(AbstractPagerImpl
.hasResultsInRange(count
, pageIndex
, pageSize
)){
417 results
= dao
.findTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraSpecificEpithet
,
418 rank
, excludedNamesUuids
,
419 pageSize
, pageIndex
, orderHints
);
421 results
= new ArrayList
<>();
424 return new DefaultPagerImpl
<TaxonNameParts
>(pageIndex
, count
, pageSize
, results
);
431 public Pager
<TaxonNameParts
> findTaxonNameParts(TaxonNamePartsFilter filter
, String namePartQueryString
,
432 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
434 return findTaxonNameParts(
435 filter
.uninomialQueryString(namePartQueryString
),
436 filter
.infraGenericEpithet(namePartQueryString
),
437 filter
.specificEpithet(namePartQueryString
),
438 filter
.infraspecificEpithet(namePartQueryString
),
440 filter
.getExludedNamesUuids(),
441 pageSize
, pageIndex
, orderHints
);
445 * TODO candidate for harmonization
446 * new name saveHomotypicalGroups
449 @Transactional(readOnly
= false)
450 public Map
<UUID
, HomotypicalGroup
> saveAllHomotypicalGroups(Collection
<HomotypicalGroup
> homotypicalGroups
){
451 return homotypicalGroupDao
.saveAll(homotypicalGroups
);
455 * TODO candidate for harmonization
456 * new name saveTypeDesignations
459 @Transactional(readOnly
= false)
460 public Map
<UUID
, TypeDesignationBase
<?
>> saveTypeDesignationAll(Collection
<TypeDesignationBase
<?
>> typeDesignationCollection
){
461 return typeDesignationDao
.saveAll(typeDesignationCollection
);
465 * TODO candidate for harmonization
466 * new name getNomenclaturalStatus
469 public List
<NomenclaturalStatus
> getAllNomenclaturalStatus(int limit
, int start
){
470 return nomStatusDao
.list(limit
, start
);
474 public NomenclaturalStatus
loadNomenclaturalStatus(UUID uuid
, List
<String
> propertyPaths
){
475 return nomStatusDao
.load(uuid
, propertyPaths
);
479 * TODO candidate for harmonization
480 * new name getTypeDesignations
483 public List
<TypeDesignationBase
<?
>> getAllTypeDesignations(int limit
, int start
){
484 return typeDesignationDao
.getAllTypeDesignations(limit
, start
);
488 public TypeDesignationBase
<?
> loadTypeDesignation(int id
, List
<String
> propertyPaths
){
489 return typeDesignationDao
.load(id
, propertyPaths
);
493 public TypeDesignationBase
<?
> loadTypeDesignation(UUID uuid
, List
<String
> propertyPaths
){
494 return typeDesignationDao
.load(uuid
, propertyPaths
);
498 * FIXME Candidate for harmonization
499 * homotypicalGroupService.list
502 public List
<HomotypicalGroup
> getAllHomotypicalGroups(int limit
, int start
){
503 return homotypicalGroupDao
.list(limit
, start
);
507 public List
<NameRelationship
> listNameRelationships(Set
<NameRelationshipType
> types
,
508 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
510 Long numberOfResults
= dao
.countNameRelationships(types
);
511 List
<NameRelationship
> results
= new ArrayList
<>();
512 if(numberOfResults
> 0) {
513 results
= dao
.getNameRelationships(types
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
519 public List
<HybridRelationship
> listHybridRelationships(Set
<HybridRelationshipType
> types
,
520 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
522 Long numberOfResults
= dao
.countHybridRelationships(types
);
523 List
<HybridRelationship
> results
= new ArrayList
<>();
524 if(numberOfResults
> 0) {
525 results
= dao
.getHybridRelationships(types
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
533 protected void setDao(ITaxonNameDao dao
) {
538 public Pager
<HybridRelationship
> getHybridNames(INonViralName name
, HybridRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
539 Integer numberOfResults
= dao
.countHybridNames(name
, type
);
541 List
<HybridRelationship
> results
= new ArrayList
<HybridRelationship
>();
542 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
543 results
= dao
.getHybridNames(name
, type
, pageSize
, pageNumber
,orderHints
,propertyPaths
);
546 return new DefaultPagerImpl
<HybridRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
550 public List
<NameRelationship
> listNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
551 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
553 Integer numberOfResults
= dao
.countNameRelationships(name
, direction
, type
);
555 List
<NameRelationship
> results
= new ArrayList
<NameRelationship
>();
556 if (AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
557 results
= dao
.getNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
563 protected LuceneSearch
prepareFindByFuzzyNameSearch(Class
<?
extends CdmBase
> clazz
,
567 List
<Language
> languages
,
568 boolean highlightFragments
) {
569 String similarity
= Float
.toString(accuracy
);
570 String searchSuffix
= "~" + similarity
;
573 Builder finalQueryBuilder
= new Builder();
574 finalQueryBuilder
.setDisableCoord(false);
575 Builder textQueryBuilder
= new Builder();
576 textQueryBuilder
.setDisableCoord(false);
578 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
579 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
581 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
582 // luceneSearch.setSortFields(sortFields);
584 // ---- search criteria
585 luceneSearch
.setCdmTypRestriction(clazz
);
587 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
588 if(nvn
.getGenusOrUninomial() != null && !nvn
.getGenusOrUninomial().equals("")) {
589 fltq
.addTerms(nvn
.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy
, 3);
591 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
592 textQueryBuilder
.add(queryFactory
.newTermQuery("genusOrUninomial", "_null_", false), Occur
.MUST
);
595 if(nvn
.getInfraGenericEpithet() != null && !nvn
.getInfraGenericEpithet().equals("")){
596 fltq
.addTerms(nvn
.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy
, 3);
598 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
599 textQueryBuilder
.add(queryFactory
.newTermQuery("infraGenericEpithet", "_null_", false), Occur
.MUST
);
602 if(nvn
.getSpecificEpithet() != null && !nvn
.getSpecificEpithet().equals("")){
603 fltq
.addTerms(nvn
.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy
, 3);
605 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
606 textQueryBuilder
.add(queryFactory
.newTermQuery("specificEpithet", "_null_", false), Occur
.MUST
);
609 if(nvn
.getInfraSpecificEpithet() != null && !nvn
.getInfraSpecificEpithet().equals("")){
610 fltq
.addTerms(nvn
.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy
, 3);
612 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
613 textQueryBuilder
.add(queryFactory
.newTermQuery("infraSpecificEpithet", "_null_", false), Occur
.MUST
);
616 if(nvn
.getAuthorshipCache() != null && !nvn
.getAuthorshipCache().equals("")){
617 fltq
.addTerms(nvn
.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy
, 3);
619 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
622 textQueryBuilder
.add(fltq
, Occur
.MUST
);
624 BooleanQuery textQuery
= textQueryBuilder
.build();
625 finalQueryBuilder
.add(textQuery
, Occur
.MUST
);
627 luceneSearch
.setQuery(finalQueryBuilder
.build());
629 if(highlightFragments
){
630 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
635 protected LuceneSearch
prepareFindByFuzzyNameCacheSearch(Class
<?
extends CdmBase
> clazz
,
639 List
<Language
> languages
,
640 boolean highlightFragments
) {
642 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
643 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
645 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
646 // luceneSearch.setSortFields(sortFields);
648 // ---- search criteria
649 luceneSearch
.setCdmTypRestriction(clazz
);
650 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
652 fltq
.addTerms(name
, "nameCache", accuracy
, 3);
654 BooleanQuery finalQuery
= new BooleanQuery(false);
656 finalQuery
.add(fltq
, Occur
.MUST
);
658 luceneSearch
.setQuery(finalQuery
);
660 if(highlightFragments
){
661 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
666 protected LuceneSearch
prepareFindByExactNameSearch(Class
<?
extends CdmBase
> clazz
,
669 List
<Language
> languages
,
670 boolean highlightFragments
) {
671 Builder textQueryBuilder
= new Builder();
673 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
674 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
676 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
677 // luceneSearch.setSortFields(sortFields);
679 // ---- search criteria
680 luceneSearch
.setCdmTypRestriction(clazz
);
682 if(name
!= null && !name
.equals("")) {
684 textQueryBuilder
.add(new WildcardQuery(new Term("nameCache", name
+ "*")), Occur
.MUST
);
686 textQueryBuilder
.add(queryFactory
.newTermQuery("nameCache", name
, false), Occur
.MUST
);
690 luceneSearch
.setQuery(textQueryBuilder
.build());
692 if(highlightFragments
){
693 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
699 public List
<SearchResult
<TaxonName
>> findByNameFuzzySearch(
702 List
<Language
> languages
,
703 boolean highlightFragments
,
704 List
<String
> propertyPaths
,
705 int maxNoOfResults
) throws IOException
, LuceneParseException
{
707 logger
.info("Name to fuzzy search for : " + name
);
708 // parse the input name
709 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
710 INonViralName nvn
= parser
.parseFullName(name
);
711 if(name
!= null && !name
.equals("") && nvn
== null) {
712 throw new LuceneParseException("Could not parse name " + name
);
714 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
716 // --- execute search
717 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
720 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
721 idFieldMap
.put(CdmBaseType
.NONVIRALNAME
, "id");
723 // --- initialize taxa, highlight matches ....
724 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
726 @SuppressWarnings("rawtypes")
727 List
<SearchResult
<TaxonName
>> searchResults
= searchResultBuilder
.createResultSet(
728 topDocs
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
730 return searchResults
;
735 public List
<DocumentSearchResult
> findByNameFuzzySearch(
738 List
<Language
> languages
,
739 boolean highlightFragments
,
740 int maxNoOfResults
) throws IOException
, LuceneParseException
{
742 logger
.info("Name to fuzzy search for : " + name
);
743 // parse the input name
744 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
745 INonViralName nvn
= parser
.parseFullName(name
);
746 if(name
!= null && !name
.equals("") && nvn
== null) {
747 throw new LuceneParseException("Could not parse name " + name
);
749 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
751 // --- execute search
752 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
754 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
756 // --- initialize taxa, highlight matches ....
757 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
759 @SuppressWarnings("rawtypes")
760 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
762 return searchResults
;
766 public List
<DocumentSearchResult
> findByFuzzyNameCacheSearch(
769 List
<Language
> languages
,
770 boolean highlightFragments
,
771 int maxNoOfResults
) throws IOException
, LuceneParseException
{
773 logger
.info("Name to fuzzy search for : " + name
);
775 LuceneSearch luceneSearch
= prepareFindByFuzzyNameCacheSearch(null, name
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
777 // --- execute search
778 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
779 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
781 // --- initialize taxa, highlight matches ....
782 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
784 @SuppressWarnings("rawtypes")
785 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
787 return searchResults
;
791 public List
<DocumentSearchResult
> findByNameExactSearch(
794 List
<Language
> languages
,
795 boolean highlightFragments
,
796 int maxNoOfResults
) throws IOException
, LuceneParseException
{
798 logger
.info("Name to exact search for : " + name
);
800 LuceneSearch luceneSearch
= prepareFindByExactNameSearch(null, name
, wildcard
, languages
, highlightFragments
);
802 // --- execute search
805 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
807 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
809 // --- initialize taxa, highlight matches ....
810 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
812 @SuppressWarnings("rawtypes")
813 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
815 return searchResults
;
819 public Pager
<NameRelationship
> pageNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
820 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
821 List
<NameRelationship
> results
= listNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
822 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
826 public List
<NameRelationship
> listFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
827 return listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
831 public Pager
<NameRelationship
> pageFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
832 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
833 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
837 public List
<NameRelationship
> listToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
838 return listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
842 public Pager
<NameRelationship
> pageToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
843 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
844 return new DefaultPagerImpl
<NameRelationship
>(pageNumber
, results
.size(), pageSize
, results
);
848 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
849 Integer pageSize
, Integer pageNumber
) {
850 return getTypeDesignations(name
, status
, pageSize
, pageNumber
, null);
854 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
855 Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
){
856 long numberOfResults
= dao
.countTypeDesignations(name
, status
);
858 List
<TypeDesignationBase
> results
= new ArrayList
<>();
859 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
, pageNumber
, pageSize
)) {
860 results
= dao
.getTypeDesignations(name
, null, status
, pageSize
, pageNumber
, propertyPaths
);
862 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
866 public List
<TypeDesignationBase
> getTypeDesignationsInHomotypicalGroup(UUID nameUuid
, Integer pageSize
,
867 Integer pageNumber
, List
<String
> propertyPaths
){
868 TaxonName name
= load(nameUuid
, Arrays
.asList("nomenclaturalReference.authorship"));
869 Set
<TypeDesignationBase
<?
>> typeDesignations
= name
.getHomotypicalGroup().getTypeDesignations();
870 List
<TypeDesignationBase
> result
= defaultBeanInitializer
.initializeAll(new ArrayList(typeDesignations
), propertyPaths
);
875 * FIXME Candidate for harmonization
879 public Pager
<TaxonName
> searchNames(String uninomial
,String infraGenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
880 List
<String
> propertyPaths
) {
881 long numberOfResults
= dao
.countNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
883 List
<TaxonName
> results
= new ArrayList
<>();
884 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
885 results
= dao
.searchNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
888 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
892 public List
<UuidAndTitleCache
> getUuidAndTitleCacheOfNames(Integer limit
, String pattern
) {
893 return dao
.getUuidAndTitleCacheOfNames(limit
, pattern
);
897 public Pager
<TaxonName
> findByName(Class
<TaxonName
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criteria
,
898 Integer pageSize
,Integer pageNumber
, List
<OrderHint
> orderHints
,List
<String
> propertyPaths
) {
899 Long numberOfResults
= dao
.countByName(clazz
, queryString
, matchmode
, criteria
);
901 List
<TaxonName
> results
= new ArrayList
<>();
902 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
903 results
= dao
.findByName(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
906 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
910 public HomotypicalGroup
findHomotypicalGroup(UUID uuid
) {
911 return homotypicalGroupDao
.findByUuid(uuid
);
915 @Transactional(readOnly
= false)
916 public UpdateResult
updateCaches(Class
<?
extends TaxonName
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonName
> cacheStrategy
, IProgressMonitor monitor
) {
918 clazz
= TaxonName
.class;
920 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
925 public List
<TaggedText
> getTaggedName(UUID uuid
) {
926 TaxonName taxonName
= dao
.load(uuid
);
927 List
<TaggedText
> taggedName
= taxonName
.getTaggedName();
932 public DeleteResult
isDeletable(TaxonName name
, DeleteConfiguratorBase config
){
933 DeleteResult result
= new DeleteResult();
935 NameDeletionConfigurator nameConfig
= null;
936 if (config
instanceof NameDeletionConfigurator
){
937 nameConfig
= (NameDeletionConfigurator
) config
;
939 result
.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
944 if (!name
.getNameRelations().isEmpty() && !nameConfig
.isRemoveAllNameRelationships()){
945 HomotypicalGroup homotypicalGroup
= HibernateProxyHelper
.deproxy(name
.getHomotypicalGroup(), HomotypicalGroup
.class);
947 if (!nameConfig
.isIgnoreIsBasionymFor() && homotypicalGroup
.getBasionyms().contains(name
)){
948 result
.addException(new Exception( "Name can't be deleted as it is a basionym."));
951 if (!nameConfig
.isIgnoreHasBasionym() && (name
.getBasionyms().size()>0)){
952 result
.addException(new Exception( "Name can't be deleted as it has a basionym."));
955 Set
<NameRelationship
> relationships
= name
.getNameRelations();
956 for (NameRelationship rel
: relationships
){
957 if (!rel
.getType().equals(NameRelationshipType
.BASIONYM())){
958 result
.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
966 if (! name
.getTaxonBases().isEmpty()){
967 result
.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
971 //hybrid relationships
972 if (name
.isNonViral()){
973 INonViralName nvn
= name
;
974 Set
<HybridRelationship
> parentHybridRelations
= nvn
.getHybridParentRelations();
975 //Hibernate.initialize(parentHybridRelations);
976 if (! parentHybridRelations
.isEmpty()){
977 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."));
981 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjectsForDeletion(name
);
982 for (CdmBase referencingObject
: referencingObjects
){
983 //DerivedUnit?.storedUnder
984 if (referencingObject
.isInstanceOf(DerivedUnit
.class)){
985 String message
= "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
986 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnit
.class).getTitleCache());
987 result
.addException(new ReferencedObjectUndeletableException(message
));
988 result
.addRelatedObject(referencingObject
);
991 //DescriptionElementSource#nameUsedInSource
992 else if (referencingObject
.isInstanceOf(DescriptionElementSource
.class)){
993 String message
= "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
994 result
.addException(new ReferencedObjectUndeletableException(message
));
995 result
.addRelatedObject(referencingObject
);
998 //NameTypeDesignation#typeName
999 else if (referencingObject
.isInstanceOf(NameTypeDesignation
.class)){
1000 NameTypeDesignation typeDesignation
= HibernateProxyHelper
.deproxy(referencingObject
, NameTypeDesignation
.class);
1002 if (typeDesignation
.getTypeName().equals(name
)){
1003 String message
= "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1004 result
.addException(new ReferencedObjectUndeletableException(message
));
1005 result
.addRelatedObject(referencingObject
);
1009 //DeterminationEvent#taxonName
1010 else if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1011 String message
= "Name can't be deleted as it is used as a determination event";
1012 result
.addException(new ReferencedObjectUndeletableException(message
));
1013 result
.addRelatedObject(referencingObject
);
1018 //TODO inline references
1021 if (!nameConfig
.isIgnoreIsReplacedSynonymFor() && name
.isReplacedSynonym()){
1022 String message
= "Name can't be deleted as it is a replaced synonym.";
1023 result
.addException(new Exception(message
));
1026 if (!nameConfig
.isIgnoreHasReplacedSynonym() && (name
.getReplacedSynonyms().size()>0)){
1027 String message
= "Name can't be deleted as it has a replaced synonym.";
1028 result
.addException(new Exception(message
));
1037 public DeleteResult
isDeletable(UUID nameUUID
, DeleteConfiguratorBase config
){
1038 TaxonName name
= this.load(nameUUID
);
1039 return isDeletable(name
, config
);
1043 @Transactional(readOnly
= true)
1044 public UpdateResult
setAsGroupsBasionym(UUID nameUuid
) {
1045 TaxonName name
= dao
.load(nameUuid
);
1046 UpdateResult result
= new UpdateResult();
1047 name
.setAsGroupsBasionym();
1048 result
.addUpdatedObject(name
);
1054 public List
<HashMap
<String
,String
>> getNameRecords(){
1055 return dao
.getNameRecords();
1060 public List
<TypeDesignationStatusBase
> getTypeDesignationStatusInUse(){
1061 return typeDesignationDao
.getTypeDesignationStatusInUse();
1065 public Collection
<TypeDesignationStatusFilter
> getTypeDesignationStatusFilterTerms(List
<Language
> preferredLanguages
){
1066 List
<TypeDesignationStatusBase
> termList
= typeDesignationDao
.getTypeDesignationStatusInUse();
1067 Map
<String
, TypeDesignationStatusFilter
> filterMap
= new HashMap
<>();
1068 for(TypeDesignationStatusBase term
: termList
){
1069 TypeDesignationStatusFilter filter
= new TypeDesignationStatusFilter(term
, preferredLanguages
, true);
1070 String key
= filter
.getKey();
1071 if(filterMap
.containsKey(key
)){
1072 filterMap
.get(key
).addStatus(term
);
1074 filterMap
.put(key
, filter
);
1077 return filterMap
.values();
1081 public <S
extends TaxonName
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1082 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1083 return page(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
1087 public <S
extends TaxonName
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1088 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includeUnpublished
) {
1091 long resultSize
= dao
.count(clazz
, restrictions
);
1092 if(AbstractPagerImpl
.hasResultsInRange(resultSize
, pageIndex
, pageSize
)){
1093 records
= dao
.list(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, includeUnpublished
);
1095 records
= new ArrayList
<>();
1097 Pager
<S
> pager
= new DefaultPagerImpl
<>(pageIndex
, resultSize
, pageSize
, records
);