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
;
23 import java
.util
.stream
.Collectors
;
25 import org
.apache
.log4j
.Logger
;
26 import org
.apache
.lucene
.index
.Term
;
27 import org
.apache
.lucene
.sandbox
.queries
.FuzzyLikeThisQuery
;
28 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
29 import org
.apache
.lucene
.search
.BooleanQuery
;
30 import org
.apache
.lucene
.search
.BooleanQuery
.Builder
;
31 import org
.apache
.lucene
.search
.TopDocs
;
32 import org
.apache
.lucene
.search
.WildcardQuery
;
33 import org
.hibernate
.criterion
.Criterion
;
34 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
35 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
36 import org
.springframework
.stereotype
.Service
;
37 import org
.springframework
.transaction
.annotation
.Transactional
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
41 import eu
.etaxonomy
.cdm
.api
.service
.dto
.TypeDesignationStatusFilter
;
42 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.AbstractPagerImpl
;
45 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
46 import eu
.etaxonomy
.cdm
.api
.service
.search
.DocumentSearchResult
;
47 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneParseException
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
54 import eu
.etaxonomy
.cdm
.api
.util
.TaxonNamePartsFilter
;
55 import eu
.etaxonomy
.cdm
.common
.URI
;
56 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
57 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
58 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
59 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
60 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
61 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
62 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
63 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
64 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
65 import eu
.etaxonomy
.cdm
.model
.common
.SourcedEntityBase
;
66 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
67 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
68 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
69 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationshipType
;
70 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
71 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
72 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationshipType
;
73 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignation
;
74 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
75 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalSource
;
76 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
77 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
78 import eu
.etaxonomy
.cdm
.model
.name
.Registration
;
79 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
80 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
81 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
82 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameFactory
;
83 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
84 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationStatusBase
;
85 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
86 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
87 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
88 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
89 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
90 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
91 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
92 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
93 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
94 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ISourcedEntityDao
;
95 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
96 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
97 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.IHomotypicalGroupDao
;
98 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.INomenclaturalStatusDao
;
99 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
100 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITypeDesignationDao
;
101 import eu
.etaxonomy
.cdm
.persistence
.dao
.reference
.IOriginalSourceDao
;
102 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNameParts
;
103 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
104 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
105 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
106 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
107 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
108 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchStrategy
;
109 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
110 import eu
.etaxonomy
.cdm
.strategy
.match
.IParsedMatchStrategy
;
111 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchException
;
112 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchStrategyFactory
;
113 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
117 @Transactional(readOnly
= true)
118 public class NameServiceImpl
119 extends IdentifiableServiceBase
<TaxonName
,ITaxonNameDao
>
120 implements INameService
{
122 static private final Logger logger
= Logger
.getLogger(NameServiceImpl
.class);
125 private IOccurrenceService occurrenceService
;
127 private ICollectionService collectionService
;
129 private ITaxonService taxonService
;
131 private ICommonService commonService
;
133 @Qualifier("sourcedEntityDao")
134 private ISourcedEntityDao
<SourcedEntityBase
<?
>> sourcedEntityDao
;
136 private INomenclaturalStatusDao nomStatusDao
;
138 private ITypeDesignationDao typeDesignationDao
;
140 private IHomotypicalGroupDao homotypicalGroupDao
;
142 private ICdmGenericDao genericDao
;
144 private ILuceneIndexToolProvider luceneIndexToolProvider
;
146 private IOriginalSourceDao sourcedDao
;
149 // @Qualifier("defaultBeanInitializer")
150 protected IBeanInitializer defaultBeanInitializer
;
152 //***************************** CONSTRUCTOR **********************************/
157 public NameServiceImpl(){}
159 //********************* METHODS ***********************************************//
162 @Transactional(readOnly
= false)
163 public DeleteResult
delete(UUID nameUUID
){
164 NameDeletionConfigurator config
= new NameDeletionConfigurator();
165 DeleteResult result
= delete(nameUUID
, config
);
170 public DeleteResult
delete(TaxonName name
){
171 return delete(name
.getUuid());
175 @Transactional(readOnly
= false)
176 public DeleteResult
delete(TaxonName name
, NameDeletionConfigurator config
) {
177 DeleteResult result
= new DeleteResult();
185 result
= this.isDeletable(name
, config
, null);
187 result
.addException(e
);
192 //remove references to this name
193 removeNameRelationshipsByDeleteConfig(name
, config
);
195 //remove name from homotypical group
196 HomotypicalGroup homotypicalGroup
= name
.getHomotypicalGroup();
197 if (homotypicalGroup
!= null){
198 homotypicalGroup
.removeTypifiedName(name
, false);
201 //all type designation relationships are removed as they belong to the name
202 deleteTypeDesignation(name
, null);
203 //if original spellings should be deleted, remove it from the nomenclatural source
204 Set
<TaxonName
> namesToUpdate
= new HashSet
<>();
205 for (Object o
: result
.getRelatedObjects()){
206 if (o
instanceof NomenclaturalSource
&& ((NomenclaturalSource
)o
).getNameUsedInSource() != null && ((NomenclaturalSource
)o
).getNameUsedInSource().equals(name
)){
207 NomenclaturalSource nomSource
= (NomenclaturalSource
)o
;
208 nomSource
.setNameUsedInSource(null);
209 namesToUpdate
.add(nomSource
.getSourcedName());
214 if (!namesToUpdate
.isEmpty()){
215 Map
<UUID
, TaxonName
> updatedNames
= dao
.saveOrUpdateAll(namesToUpdate
);
216 Set
<TaxonName
> names
= new HashSet
<>(updatedNames
.values());
217 result
.addUpdatedObjects(names
);
220 result
.addDeletedObject(name
);
223 result
.addException(e
);
233 @Transactional(readOnly
= false)
234 public DeleteResult
delete(UUID nameUUID
, NameDeletionConfigurator config
) {
236 TaxonName name
= dao
.load(nameUUID
);
237 return delete(name
, config
);
241 @Transactional(readOnly
= false)
242 public UpdateResult
cloneTypeDesignation(UUID nameUuid
, SpecimenTypeDesignation baseDesignation
,
243 String accessionNumber
, String barcode
, String catalogNumber
,
244 UUID collectionUuid
, SpecimenTypeDesignationStatus typeStatus
, URI preferredStableUri
){
245 UpdateResult result
= new UpdateResult();
247 DerivedUnit baseSpecimen
= HibernateProxyHelper
.deproxy(occurrenceService
.load(baseDesignation
.getTypeSpecimen().getUuid(), Arrays
.asList("collection")), DerivedUnit
.class);
248 DerivedUnit duplicate
= DerivedUnit
.NewInstance(baseSpecimen
.getRecordBasis());
249 DerivationEvent derivedFrom
= baseSpecimen
.getDerivedFrom();
250 Collection
<FieldUnit
> fieldUnits
= occurrenceService
.findFieldUnits(baseSpecimen
.getUuid(), null);
251 if(fieldUnits
.size()!=1){
252 result
.addException(new Exception("More than one or no field unit found for specimen"));
256 for (SpecimenOrObservationBase original
: derivedFrom
.getOriginals()) {
257 DerivationEvent
.NewSimpleInstance(original
, duplicate
, derivedFrom
.getType());
260 duplicate
.setAccessionNumber(accessionNumber
);
261 duplicate
.setBarcode(barcode
);
262 duplicate
.setCatalogNumber(catalogNumber
);
263 duplicate
.setCollection(collectionService
.load(collectionUuid
));
264 SpecimenTypeDesignation typeDesignation
= SpecimenTypeDesignation
.NewInstance();
265 typeDesignation
.setTypeSpecimen(duplicate
);
266 typeDesignation
.setTypeStatus(typeStatus
);
267 typeDesignation
.getTypeSpecimen().setPreferredStableUri(preferredStableUri
);
269 TaxonName name
= load(nameUuid
);
270 name
.getTypeDesignations().add(typeDesignation
);
272 result
.setCdmEntity(typeDesignation
);
273 result
.addUpdatedObject(name
);
279 public DeleteResult
deleteTypeDesignation(TaxonName name
, TypeDesignationBase
<?
> typeDesignation
){
280 if(typeDesignation
!= null && typeDesignation
.isPersited()){
281 typeDesignation
= HibernateProxyHelper
.deproxy(sourcedEntityDao
.load(typeDesignation
.getUuid()), TypeDesignationBase
.class);
284 DeleteResult result
= new DeleteResult();
285 if (name
== null && typeDesignation
== null){
288 }else if (name
!= null && typeDesignation
!= null){
289 removeSingleDesignation(name
, typeDesignation
);
290 }else if (name
!= null){
291 @SuppressWarnings("rawtypes")
292 Set
<TypeDesignationBase
<?
>> designationSet
= new HashSet(name
.getTypeDesignations());
293 for (TypeDesignationBase
<?
> desig
: designationSet
){
294 desig
= CdmBase
.deproxy(desig
);
295 removeSingleDesignation(name
, desig
);
297 }else if (typeDesignation
!= null){
298 @SuppressWarnings("unchecked")
299 Set
<TaxonName
> nameSet
= new HashSet(typeDesignation
.getTypifiedNames());
300 for (TaxonName singleName
: nameSet
){
301 singleName
= CdmBase
.deproxy(singleName
);
302 removeSingleDesignation(singleName
, typeDesignation
);
305 result
.addDeletedObject(typeDesignation
);
306 result
.addUpdatedObject(name
);
312 @Transactional(readOnly
= false)
313 public DeleteResult
deleteTypeDesignation(UUID nameUuid
, UUID typeDesignationUuid
){
314 TaxonName nameBase
= load(nameUuid
);
315 TypeDesignationBase
<?
> typeDesignation
= HibernateProxyHelper
.deproxy(sourcedEntityDao
.load(typeDesignationUuid
), TypeDesignationBase
.class);
316 return deleteTypeDesignation(nameBase
, typeDesignation
);
321 * @param typeDesignation
324 private void removeSingleDesignation(TaxonName name
, TypeDesignationBase
<?
> typeDesignation
) {
326 name
.removeTypeDesignation(typeDesignation
);
327 if (typeDesignation
.getTypifiedNames().isEmpty()){
328 typeDesignation
.removeType();
329 if (!typeDesignation
.getRegistrations().isEmpty()){
330 for(Object reg
: typeDesignation
.getRegistrations()){
331 if (reg
instanceof Registration
){
332 ((Registration
)reg
).removeTypeDesignation(typeDesignation
);
337 typeDesignationDao
.delete(typeDesignation
);
348 private void removeNameRelationshipsByDeleteConfig(TaxonName name
, NameDeletionConfigurator config
) {
350 if (config
.isRemoveAllNameRelationships()){
351 Set
<NameRelationship
> rels
= getModifiableSet(name
.getNameRelations());
352 for (NameRelationship rel
: rels
){
353 name
.removeNameRelationship(rel
);
356 //relations to this name
357 Set
<NameRelationship
> rels
= getModifiableSet(name
.getRelationsToThisName());
358 for (NameRelationship rel
: rels
){
359 if (config
.isIgnoreHasBasionym() && NameRelationshipType
.BASIONYM().equals(rel
.getType() )){
360 name
.removeNameRelationship(rel
);
361 }else if (config
.isIgnoreHasReplacedSynonym() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
362 name
.removeNameRelationship(rel
);
365 //relations from this name
366 rels
= getModifiableSet(name
.getRelationsFromThisName());
367 for (NameRelationship rel
: rels
){
368 if (config
.isIgnoreIsBasionymFor() && NameRelationshipType
.BASIONYM().equals(rel
.getType()) ){
369 name
.removeNameRelationship(rel
);
370 }else if (config
.isIgnoreIsReplacedSynonymFor() && NameRelationshipType
.REPLACED_SYNONYM().equals(rel
.getType())){
371 name
.removeNameRelationship(rel
);
376 } catch (Exception e
) {
377 throw new RuntimeException(e
);
385 private Set
<NameRelationship
> getModifiableSet(Set
<NameRelationship
> relations
) {
386 Set
<NameRelationship
> rels
= new HashSet
<NameRelationship
>();
387 for (NameRelationship rel
: relations
){
393 //********************* METHODS ****************************************************************//
397 * TODO candidate for harmonization
398 * new name findByName
402 public List
<TaxonName
> getNamesByNameCache(String nameCache
){
403 boolean includeAuthors
= false;
404 List result
= dao
.findByName(includeAuthors
, nameCache
, MatchMode
.EXACT
, null, null, null, null);
409 * TODO candidate for harmonization
410 * new name saveHomotypicalGroups
416 public List
<TaxonName
> findNamesByTitleCache(String titleCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
417 List
<TaxonName
> result
= dao
.findByTitle(titleCache
, matchMode
, null, null, null, propertyPaths
);
422 * TODO candidate for harmonization
423 * new name saveHomotypicalGroups
429 public List
<TaxonName
> findNamesByNameCache(String nameCache
, MatchMode matchMode
, List
<String
> propertyPaths
){
430 List
<TaxonName
> result
= dao
.findByName(false, nameCache
, matchMode
, null, null, null , propertyPaths
);
435 public Pager
<TaxonNameParts
> findTaxonNameParts(Optional
<String
> genusOrUninomial
,
436 Optional
<String
> infraGenericEpithet
, Optional
<String
> specificEpithet
,
437 Optional
<String
> infraSpecificEpithet
, Rank rank
, Set
<UUID
> excludedNamesUuids
,
438 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
441 long count
= dao
.countTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraGenericEpithet
, rank
, excludedNamesUuids
);
443 List
<TaxonNameParts
> results
;
444 if(AbstractPagerImpl
.hasResultsInRange(count
, pageIndex
, pageSize
)){
445 results
= dao
.findTaxonNameParts(genusOrUninomial
, infraGenericEpithet
, specificEpithet
, infraSpecificEpithet
,
446 rank
, excludedNamesUuids
,
447 pageSize
, pageIndex
, orderHints
);
449 results
= new ArrayList
<>();
452 return new DefaultPagerImpl
<TaxonNameParts
>(pageIndex
, count
, pageSize
, results
);
459 public Pager
<TaxonNameParts
> findTaxonNameParts(TaxonNamePartsFilter filter
, String namePartQueryString
,
460 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
462 return findTaxonNameParts(
463 filter
.uninomialQueryString(namePartQueryString
),
464 filter
.infraGenericEpithet(namePartQueryString
),
465 filter
.specificEpithet(namePartQueryString
),
466 filter
.infraspecificEpithet(namePartQueryString
),
468 filter
.getExludedNamesUuids(),
469 pageSize
, pageIndex
, orderHints
);
473 * TODO candidate for harmonization
474 * new name saveHomotypicalGroups
477 @Transactional(readOnly
= false)
478 public Map
<UUID
, HomotypicalGroup
> saveAllHomotypicalGroups(Collection
<HomotypicalGroup
> homotypicalGroups
){
479 return homotypicalGroupDao
.saveAll(homotypicalGroups
);
483 * TODO candidate for harmonization
484 * new name saveTypeDesignations
487 @Transactional(readOnly
= false)
488 public Map
<UUID
, TypeDesignationBase
<?
>> saveTypeDesignationAll(Collection
<TypeDesignationBase
<?
>> typeDesignationCollection
){
489 return typeDesignationDao
.saveAll(typeDesignationCollection
);
493 * TODO candidate for harmonization
494 * new name getNomenclaturalStatus
497 public List
<NomenclaturalStatus
> getAllNomenclaturalStatus(int limit
, int start
){
498 return nomStatusDao
.list(limit
, start
);
502 public NomenclaturalStatus
loadNomenclaturalStatus(UUID uuid
, List
<String
> propertyPaths
){
503 return nomStatusDao
.load(uuid
, propertyPaths
);
507 * TODO candidate for harmonization
508 * new name getTypeDesignations
511 public List
<TypeDesignationBase
<?
>> getAllTypeDesignations(int limit
, int start
){
512 return typeDesignationDao
.getAllTypeDesignations(limit
, start
);
516 public TypeDesignationBase
<?
> loadTypeDesignation(int id
, List
<String
> propertyPaths
){
517 return typeDesignationDao
.load(id
, propertyPaths
);
521 public TypeDesignationBase
<?
> loadTypeDesignation(UUID uuid
, List
<String
> propertyPaths
){
522 return typeDesignationDao
.load(uuid
, propertyPaths
);
526 public List
<TypeDesignationBase
<?
>> loadTypeDesignations(List
<UUID
> uuids
, List
<String
> propertyPaths
){
531 List
<TypeDesignationBase
<?
>> entities
= new ArrayList
<>();
532 for(UUID uuid
: uuids
) {
533 entities
.add(uuid
== null ?
null : typeDesignationDao
.load(uuid
, propertyPaths
));
539 * FIXME Candidate for harmonization
540 * homotypicalGroupService.list
543 public List
<HomotypicalGroup
> getAllHomotypicalGroups(int limit
, int start
){
544 return homotypicalGroupDao
.list(limit
, start
);
549 public List
<NomenclaturalSource
> listOriginalSpellings(Integer pageSize
, Integer pageNumber
,
550 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
552 Long numberOfResults
= sourcedDao
.countWithNameUsedInSource(NomenclaturalSource
.class);
553 List
<NomenclaturalSource
> results
= new ArrayList
<>();
554 if(numberOfResults
> 0) {
555 results
= sourcedDao
.listWithNameUsedInSource(NomenclaturalSource
.class, pageSize
, pageNumber
, orderHints
, propertyPaths
);
561 public List
<NameRelationship
> listNameRelationships(Set
<NameRelationshipType
> types
,
562 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
564 Long numberOfResults
= dao
.countNameRelationships(types
);
565 List
<NameRelationship
> results
= new ArrayList
<>();
566 if(numberOfResults
> 0) {
567 results
= dao
.getNameRelationships(types
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
573 public List
<HybridRelationship
> listHybridRelationships(Set
<HybridRelationshipType
> types
,
574 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
576 Long numberOfResults
= dao
.countHybridRelationships(types
);
577 List
<HybridRelationship
> results
= new ArrayList
<>();
578 if(numberOfResults
> 0) {
579 results
= dao
.getHybridRelationships(types
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
587 protected void setDao(ITaxonNameDao dao
) {
592 public Pager
<HybridRelationship
> getHybridNames(INonViralName name
, HybridRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
593 Integer numberOfResults
= dao
.countHybridNames(name
, type
);
595 List
<HybridRelationship
> results
= new ArrayList
<HybridRelationship
>();
596 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
597 results
= dao
.getHybridNames(name
, type
, pageSize
, pageNumber
,orderHints
,propertyPaths
);
600 return new DefaultPagerImpl
<HybridRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
604 public List
<NameRelationship
> listNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
605 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
607 Integer numberOfResults
= dao
.countNameRelationships(name
, direction
, type
);
609 List
<NameRelationship
> results
= new ArrayList
<NameRelationship
>();
610 if (AbstractPagerImpl
.hasResultsInRange(numberOfResults
.longValue(), pageNumber
, pageSize
)) { // no point checking again
611 results
= dao
.getNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
617 protected LuceneSearch
prepareFindByFuzzyNameSearch(Class
<?
extends CdmBase
> clazz
,
621 List
<Language
> languages
,
622 boolean highlightFragments
) {
623 String similarity
= Float
.toString(accuracy
);
624 String searchSuffix
= "~" + similarity
;
627 Builder finalQueryBuilder
= new Builder();
628 finalQueryBuilder
.setDisableCoord(false);
629 Builder textQueryBuilder
= new Builder();
630 textQueryBuilder
.setDisableCoord(false);
632 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
633 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
635 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
636 // luceneSearch.setSortFields(sortFields);
638 // ---- search criteria
639 luceneSearch
.setCdmTypRestriction(clazz
);
641 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
642 if(nvn
.getGenusOrUninomial() != null && !nvn
.getGenusOrUninomial().equals("")) {
643 fltq
.addTerms(nvn
.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy
, 3);
645 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
646 textQueryBuilder
.add(queryFactory
.newTermQuery("genusOrUninomial", "_null_", false), Occur
.MUST
);
649 if(nvn
.getInfraGenericEpithet() != null && !nvn
.getInfraGenericEpithet().equals("")){
650 fltq
.addTerms(nvn
.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy
, 3);
652 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
653 textQueryBuilder
.add(queryFactory
.newTermQuery("infraGenericEpithet", "_null_", false), Occur
.MUST
);
656 if(nvn
.getSpecificEpithet() != null && !nvn
.getSpecificEpithet().equals("")){
657 fltq
.addTerms(nvn
.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy
, 3);
659 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
660 textQueryBuilder
.add(queryFactory
.newTermQuery("specificEpithet", "_null_", false), Occur
.MUST
);
663 if(nvn
.getInfraSpecificEpithet() != null && !nvn
.getInfraSpecificEpithet().equals("")){
664 fltq
.addTerms(nvn
.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy
, 3);
666 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
667 textQueryBuilder
.add(queryFactory
.newTermQuery("infraSpecificEpithet", "_null_", false), Occur
.MUST
);
670 if(nvn
.getAuthorshipCache() != null && !nvn
.getAuthorshipCache().equals("")){
671 fltq
.addTerms(nvn
.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy
, 3);
673 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
676 textQueryBuilder
.add(fltq
, Occur
.MUST
);
678 BooleanQuery textQuery
= textQueryBuilder
.build();
679 finalQueryBuilder
.add(textQuery
, Occur
.MUST
);
681 luceneSearch
.setQuery(finalQueryBuilder
.build());
683 if(highlightFragments
){
684 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
689 protected LuceneSearch
prepareFindByFuzzyNameCacheSearch(Class
<?
extends CdmBase
> clazz
,
693 List
<Language
> languages
,
694 boolean highlightFragments
) {
696 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
697 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
699 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
700 // luceneSearch.setSortFields(sortFields);
702 // ---- search criteria
703 luceneSearch
.setCdmTypRestriction(clazz
);
704 FuzzyLikeThisQuery fltq
= new FuzzyLikeThisQuery(maxNoOfResults
, luceneSearch
.getAnalyzer());
706 fltq
.addTerms(name
, "nameCache", accuracy
, 3);
708 BooleanQuery finalQuery
= new BooleanQuery(false);
710 finalQuery
.add(fltq
, Occur
.MUST
);
712 luceneSearch
.setQuery(finalQuery
);
714 if(highlightFragments
){
715 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
720 protected LuceneSearch
prepareFindByExactNameSearch(Class
<?
extends CdmBase
> clazz
,
723 List
<Language
> languages
,
724 boolean highlightFragments
) {
725 Builder textQueryBuilder
= new Builder();
727 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonName
.class);
728 QueryFactory queryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonName
.class);
730 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
731 // luceneSearch.setSortFields(sortFields);
733 // ---- search criteria
734 luceneSearch
.setCdmTypRestriction(clazz
);
736 if(name
!= null && !name
.equals("")) {
738 textQueryBuilder
.add(new WildcardQuery(new Term("nameCache", name
+ "*")), Occur
.MUST
);
740 textQueryBuilder
.add(queryFactory
.newTermQuery("nameCache", name
, false), Occur
.MUST
);
744 luceneSearch
.setQuery(textQueryBuilder
.build());
746 if(highlightFragments
){
747 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
753 public List
<SearchResult
<TaxonName
>> findByNameFuzzySearch(
756 List
<Language
> languages
,
757 boolean highlightFragments
,
758 List
<String
> propertyPaths
,
759 int maxNoOfResults
) throws IOException
, LuceneParseException
{
761 logger
.info("Name to fuzzy search for : " + name
);
762 // parse the input name
763 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
764 INonViralName nvn
= parser
.parseFullName(name
);
765 if(name
!= null && !name
.equals("") && nvn
== null) {
766 throw new LuceneParseException("Could not parse name " + name
);
768 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
770 // --- execute search
771 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
774 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
775 idFieldMap
.put(CdmBaseType
.TAXON_NAME
, "id");
777 // --- initialize taxa, highlight matches ....
778 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
780 @SuppressWarnings("rawtypes")
781 List
<SearchResult
<TaxonName
>> searchResults
= searchResultBuilder
.createResultSet(
782 topDocs
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
784 return searchResults
;
789 public List
<DocumentSearchResult
> findByNameFuzzySearch(
792 List
<Language
> languages
,
793 boolean highlightFragments
,
794 int maxNoOfResults
) throws IOException
, LuceneParseException
{
796 logger
.info("Name to fuzzy search for : " + name
);
797 // parse the input name
798 NonViralNameParserImpl parser
= new NonViralNameParserImpl();
799 INonViralName nvn
= parser
.parseFullName(name
);
800 if(name
!= null && !name
.equals("") && nvn
== null) {
801 throw new LuceneParseException("Could not parse name " + name
);
803 LuceneSearch luceneSearch
= prepareFindByFuzzyNameSearch(null, nvn
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
805 // --- execute search
806 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
808 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
810 // --- initialize taxa, highlight matches ....
811 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
813 @SuppressWarnings("rawtypes")
814 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
816 return searchResults
;
820 public List
<DocumentSearchResult
> findByFuzzyNameCacheSearch(
823 List
<Language
> languages
,
824 boolean highlightFragments
,
825 int maxNoOfResults
) throws IOException
, LuceneParseException
{
827 logger
.info("Name to fuzzy search for : " + name
);
829 LuceneSearch luceneSearch
= prepareFindByFuzzyNameCacheSearch(null, name
, accuracy
, maxNoOfResults
, languages
, highlightFragments
);
831 // --- execute search
832 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
833 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
835 // --- initialize taxa, highlight matches ....
836 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
838 @SuppressWarnings("rawtypes")
839 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
841 return searchResults
;
845 public List
<DocumentSearchResult
> findByNameExactSearch(
848 List
<Language
> languages
,
849 boolean highlightFragments
,
850 int maxNoOfResults
) throws IOException
, LuceneParseException
{
852 logger
.info("Name to exact search for : " + name
);
854 LuceneSearch luceneSearch
= prepareFindByExactNameSearch(null, name
, wildcard
, languages
, highlightFragments
);
856 // --- execute search
859 TopDocs topDocs
= luceneSearch
.executeSearch(maxNoOfResults
);
861 // --- initialize taxa, highlight matches ....
862 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
864 List
<DocumentSearchResult
> searchResults
= searchResultBuilder
.createResultSet(topDocs
, luceneSearch
.getHighlightFields());
866 return searchResults
;
870 public Pager
<NameRelationship
> pageNameRelationships(TaxonName name
, Direction direction
, NameRelationshipType type
, Integer pageSize
,
871 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
872 List
<NameRelationship
> results
= listNameRelationships(name
, direction
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
873 return new DefaultPagerImpl
<>(pageNumber
, results
.size(), pageSize
, results
);
877 public List
<NameRelationship
> listFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
878 return listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
882 public Pager
<NameRelationship
> pageFromNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
883 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedFrom
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
884 return new DefaultPagerImpl
<>(pageNumber
, results
.size(), pageSize
, results
);
888 public List
<NameRelationship
> listToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
889 return listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
893 public Pager
<NameRelationship
> pageToNameRelationships(TaxonName name
, NameRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
894 List
<NameRelationship
> results
= listNameRelationships(name
, Direction
.relatedTo
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
895 return new DefaultPagerImpl
<>(pageNumber
, results
.size(), pageSize
, results
);
899 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
900 Integer pageSize
, Integer pageNumber
) {
901 return getTypeDesignations(name
, status
, pageSize
, pageNumber
, null);
905 public Pager
<TypeDesignationBase
> getTypeDesignations(TaxonName name
, SpecimenTypeDesignationStatus status
,
906 Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
){
907 long numberOfResults
= dao
.countTypeDesignations(name
, status
);
909 List
<TypeDesignationBase
> results
= new ArrayList
<>();
910 if(AbstractPagerImpl
.hasResultsInRange(numberOfResults
, pageNumber
, pageSize
)) {
911 results
= dao
.getTypeDesignations(name
, null, status
, pageSize
, pageNumber
, propertyPaths
);
913 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
917 public List
<TypeDesignationBase
> getTypeDesignationsInHomotypicalGroup(UUID nameUuid
, Integer pageSize
,
918 Integer pageNumber
, List
<String
> propertyPaths
){
919 TaxonName name
= load(nameUuid
, Arrays
.asList("nomenclaturalSource.citation.authorship"));
920 Set
<TypeDesignationBase
<?
>> typeDesignations
= name
.getHomotypicalGroup().getTypeDesignations();
921 List
<TypeDesignationBase
> result
= defaultBeanInitializer
.initializeAll(new ArrayList(typeDesignations
), propertyPaths
);
926 * FIXME Candidate for harmonization
930 public Pager
<TaxonName
> searchNames(String uninomial
,String infraGenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
931 List
<String
> propertyPaths
) {
932 long numberOfResults
= dao
.countNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
934 List
<TaxonName
> results
= new ArrayList
<>();
935 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
936 results
= dao
.searchNames(uninomial
, infraGenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
939 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
943 public List
<UuidAndTitleCache
> getUuidAndTitleCacheOfNames(Integer limit
, String pattern
) {
944 return dao
.getUuidAndTitleCacheOfNames(limit
, pattern
);
948 public Pager
<TaxonName
> findByName(Class
<TaxonName
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criteria
,
949 Integer pageSize
,Integer pageNumber
, List
<OrderHint
> orderHints
,List
<String
> propertyPaths
) {
950 Long numberOfResults
= dao
.countByName(clazz
, queryString
, matchmode
, criteria
);
952 List
<TaxonName
> results
= new ArrayList
<>();
953 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
954 results
= dao
.findByName(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
957 return new DefaultPagerImpl
<>(pageNumber
, numberOfResults
, pageSize
, results
);
961 public List
<TaxonName
> findByFullTitle(Class
<TaxonName
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criteria
,
962 Integer pageSize
,Integer pageNumber
, List
<OrderHint
> orderHints
,List
<String
> propertyPaths
) {
963 Long numberOfResults
= dao
.countByFullTitle(clazz
, queryString
, matchmode
, criteria
);
965 List
<TaxonName
> results
= new ArrayList
<>();
966 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
967 results
= dao
.findByFullTitle(queryString
, matchmode
, pageSize
, pageNumber
, criteria
, propertyPaths
);
975 public HomotypicalGroup
findHomotypicalGroup(UUID uuid
) {
976 return homotypicalGroupDao
.findByUuid(uuid
);
980 @Transactional(readOnly
= false)
981 public UpdateResult
updateCaches(Class
<?
extends TaxonName
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonName
> cacheStrategy
, IProgressMonitor monitor
) {
983 clazz
= TaxonName
.class;
985 return super.updateCachesImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
990 public List
<TaggedText
> getTaggedName(UUID uuid
) {
991 TaxonName taxonName
= dao
.load(uuid
);
992 List
<TaggedText
> taggedName
= taxonName
.getTaggedName();
997 public DeleteResult
isDeletable(TaxonName name
, DeleteConfiguratorBase config
, UUID taxonUuid
){
998 DeleteResult result
= new DeleteResult();
1000 NameDeletionConfigurator nameConfig
= null;
1001 if (config
instanceof NameDeletionConfigurator
){
1002 nameConfig
= (NameDeletionConfigurator
) config
;
1004 result
.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
1009 if (!name
.getNameRelations().isEmpty() && !nameConfig
.isRemoveAllNameRelationships()){
1010 HomotypicalGroup homotypicalGroup
= HibernateProxyHelper
.deproxy(name
.getHomotypicalGroup(), HomotypicalGroup
.class);
1012 if (!nameConfig
.isIgnoreIsBasionymFor() && homotypicalGroup
.getBasionyms().contains(name
)){
1013 result
.addException(new Exception( "Name can't be deleted as it is a basionym."));
1016 if (!nameConfig
.isIgnoreHasBasionym() && (name
.getBasionyms().size()>0)){
1017 result
.addException(new Exception( "Name can't be deleted as it has a basionym."));
1020 Set
<NameRelationship
> relationships
= name
.getNameRelations();
1021 for (NameRelationship rel
: relationships
){
1022 if (!rel
.getType().equals(NameRelationshipType
.BASIONYM())){
1023 result
.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
1030 if (!name
.getTaxonBases().isEmpty()){
1031 boolean isDeletableTaxon
= true;
1032 List
<TaxonBase
> notDeletedTaxonBases
= name
.getTaxonBases().stream()
1033 .filter((taxonBase
) -> !taxonBase
.getUuid().equals(taxonUuid
))
1034 .collect(Collectors
.toList());
1035 if (!notDeletedTaxonBases
.isEmpty()){
1036 result
.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
1041 //hybrid relationships
1042 if (name
.isNonViral()){
1043 INonViralName nvn
= name
;
1044 Set
<HybridRelationship
> parentHybridRelations
= nvn
.getHybridParentRelations();
1045 //Hibernate.initialize(parentHybridRelations);
1046 if (! parentHybridRelations
.isEmpty()){
1047 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."));
1051 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjectsForDeletion(name
);
1052 for (CdmBase referencingObject
: referencingObjects
){
1053 //DerivedUnit?.storedUnder
1054 if (referencingObject
.isInstanceOf(DerivedUnit
.class)){
1055 String message
= "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
1056 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnit
.class).getTitleCache());
1057 result
.addException(new ReferencedObjectUndeletableException(message
));
1058 result
.addRelatedObject(referencingObject
);
1062 //DescriptionElementSource#nameUsedInSource
1063 else if (referencingObject
.isInstanceOf(DescriptionElementSource
.class) && !referencingObject
.isInstanceOf(NomenclaturalSource
.class) ){
1064 String message
= "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
1065 result
.addException(new ReferencedObjectUndeletableException(message
));
1066 result
.addRelatedObject(referencingObject
);
1069 //NameTypeDesignation#typeName
1070 else if (referencingObject
.isInstanceOf(NameTypeDesignation
.class)){
1071 NameTypeDesignation typeDesignation
= HibernateProxyHelper
.deproxy(referencingObject
, NameTypeDesignation
.class);
1073 if (typeDesignation
.getTypeName().equals(name
) && !typeDesignation
.getTypifiedNames().isEmpty()){
1074 String message
= "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
1075 result
.addException(new ReferencedObjectUndeletableException(message
));
1076 result
.addRelatedObject(referencingObject
);
1080 //DeterminationEvent#taxonName
1081 else if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1082 String message
= "Name can't be deleted as it is used as a determination event";
1083 result
.addException(new ReferencedObjectUndeletableException(message
));
1084 result
.addRelatedObject(referencingObject
);
1088 else if (referencingObject
.isInstanceOf(NomenclaturalSource
.class) && !((NameDeletionConfigurator
)config
).isIgnoreIsOriginalSpellingFor()){
1089 if (((NomenclaturalSource
)referencingObject
).getNameUsedInSource() != null && ((NomenclaturalSource
)referencingObject
).getNameUsedInSource().equals(name
)){
1090 String message
= "Name can't be deleted as it is used as original spelling";
1091 result
.addException(new ReferencedObjectUndeletableException(message
));
1092 result
.addRelatedObject(referencingObject
);
1096 if (referencingObject
.isInstanceOf(NomenclaturalSource
.class)){
1097 if (((NomenclaturalSource
)referencingObject
).getNameUsedInSource() != null && ((NomenclaturalSource
)referencingObject
).getNameUsedInSource().equals(name
)){
1098 result
.addRelatedObject(referencingObject
);
1104 //TODO inline references
1107 if (!nameConfig
.isIgnoreIsReplacedSynonymFor() && name
.isReplacedSynonym()){
1108 String message
= "Name can't be deleted as it is a replaced synonym.";
1109 result
.addException(new Exception(message
));
1112 if (!nameConfig
.isIgnoreHasReplacedSynonym() && (name
.getReplacedSynonyms().size()>0)){
1113 String message
= "Name can't be deleted as it has a replaced synonym.";
1114 result
.addException(new Exception(message
));
1123 public DeleteResult
isDeletable(UUID nameUUID
, DeleteConfiguratorBase config
){
1124 TaxonName name
= this.load(nameUUID
);
1125 return isDeletable(name
, config
, null);
1129 public DeleteResult
isDeletable(UUID nameUUID
, DeleteConfiguratorBase config
, UUID taxonUuid
){
1130 TaxonName name
= this.load(nameUUID
);
1131 return isDeletable(name
, config
, taxonUuid
);
1135 @Transactional(readOnly
= true)
1136 public UpdateResult
setAsGroupsBasionym(UUID nameUuid
) {
1137 TaxonName name
= dao
.load(nameUuid
);
1138 UpdateResult result
= new UpdateResult();
1139 name
.setAsGroupsBasionym();
1140 result
.addUpdatedObject(name
);
1146 public List
<HashMap
<String
,String
>> getNameRecords(){
1147 return dao
.getNameRecords();
1152 public List
<TypeDesignationStatusBase
> getTypeDesignationStatusInUse(){
1153 return typeDesignationDao
.getTypeDesignationStatusInUse();
1157 public Collection
<TypeDesignationStatusFilter
> getTypeDesignationStatusFilterTerms(List
<Language
> preferredLanguages
){
1158 List
<TypeDesignationStatusBase
> termList
= typeDesignationDao
.getTypeDesignationStatusInUse();
1159 Map
<String
, TypeDesignationStatusFilter
> filterMap
= new HashMap
<>();
1160 for(TypeDesignationStatusBase term
: termList
){
1161 TypeDesignationStatusFilter filter
= new TypeDesignationStatusFilter(term
, preferredLanguages
, true);
1162 String key
= filter
.getKey();
1163 if(filterMap
.containsKey(key
)){
1164 filterMap
.get(key
).addStatus(term
);
1166 filterMap
.put(key
, filter
);
1169 return filterMap
.values();
1173 public <S
extends TaxonName
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1174 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1175 return page(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
1179 public <S
extends TaxonName
> Pager
<S
> page(Class
<S
> clazz
, List
<Restriction
<?
>> restrictions
, Integer pageSize
,
1180 Integer pageIndex
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includeUnpublished
) {
1183 long resultSize
= dao
.count(clazz
, restrictions
);
1184 if(AbstractPagerImpl
.hasResultsInRange(resultSize
, pageIndex
, pageSize
)){
1185 records
= dao
.list(clazz
, restrictions
, pageSize
, pageIndex
, orderHints
, propertyPaths
, includeUnpublished
);
1187 records
= new ArrayList
<>();
1189 Pager
<S
> pager
= new DefaultPagerImpl
<>(pageIndex
, resultSize
, pageSize
, records
);
1194 public List
<UuidAndTitleCache
> getUuidAndTitleCacheOfSynonymy(Integer limit
, UUID taxonUuid
) {
1195 List
<String
> propertyPaths
= new ArrayList
<>();
1196 propertyPaths
.add("synonyms.name.*");
1197 TaxonBase
<?
> taxonBase
= taxonService
.load(taxonUuid
, propertyPaths
);
1198 if (taxonBase
instanceof Taxon
){
1199 Taxon taxon
= (Taxon
)taxonBase
;
1200 Set
<TaxonName
> names
= taxon
.getSynonymNames();
1201 List
<UuidAndTitleCache
> uuidAndTitleCacheList
= new ArrayList
<>();
1202 UuidAndTitleCache
<TaxonName
> uuidAndTitleCache
;
1203 for (TaxonName name
: names
){
1204 uuidAndTitleCache
= new UuidAndTitleCache
<TaxonName
>(TaxonName
.class, name
.getUuid(), name
.getId(), name
.getTitleCache());
1205 uuidAndTitleCacheList
.add(uuidAndTitleCache
);
1212 public UpdateResult
parseName(String stringToBeParsed
, NomenclaturalCode code
, Rank preferredRank
, boolean doDeduplicate
) {
1213 TaxonName name
= TaxonNameFactory
.NewNameInstance(code
, preferredRank
);
1214 return parseName(name
, stringToBeParsed
, preferredRank
, true, doDeduplicate
);
1218 public UpdateResult
parseName(TaxonName nameToBeFilled
, String stringToBeParsed
, Rank preferredRank
,
1219 boolean doEmpty
, boolean doDeduplicate
){
1221 UpdateResult result
= new UpdateResult();
1222 NonViralNameParserImpl nonViralNameParser
= NonViralNameParserImpl
.NewInstance();
1223 nonViralNameParser
.parseReferencedName(nameToBeFilled
, stringToBeParsed
, preferredRank
, doEmpty
);
1224 TaxonName name
= nameToBeFilled
;
1227 // Level sqlLogLevel = Logger.getLogger("org.hibernate.SQL").getLevel();
1228 // Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
1231 if (name
.getNomenclaturalReference()!= null && !name
.getNomenclaturalReference().isPersited()){
1232 Reference nomRef
= name
.getNomenclaturalReference();
1233 IMatchStrategy referenceMatcher
= MatchStrategyFactory
.NewParsedReferenceInstance(nomRef
);
1234 List
<Reference
> matchingReferences
= commonService
.findMatching(nomRef
, referenceMatcher
);
1235 if(matchingReferences
.size() >= 1){
1236 Reference duplicate
= findBestMatching(nomRef
, matchingReferences
, referenceMatcher
);
1237 name
.setNomenclaturalReference(duplicate
);
1239 if (nomRef
.getInReference() != null){
1240 List
<Reference
> matchingInReferences
= commonService
.findMatching(nomRef
.getInReference(), MatchStrategyFactory
.NewParsedReferenceInstance(nomRef
.getInReference()));
1241 if(matchingInReferences
.size() >= 1){
1242 Reference duplicate
= findBestMatching(nomRef
, matchingInReferences
, referenceMatcher
);
1243 nomRef
.setInReference(duplicate
);
1246 TeamOrPersonBase
<?
> author
= deduplicateAuthor(nomRef
.getAuthorship());
1247 nomRef
.setAuthorship(author
);
1250 Reference nomRef
= name
.getNomenclaturalReference();
1253 IParsedMatchStrategy authorMatcher
= MatchStrategyFactory
.NewParsedTeamOrPersonInstance();
1254 if (name
.getCombinationAuthorship()!= null && !name
.getCombinationAuthorship().isPersited()){
1255 //use same nom.ref. author if possible (should always be possible if nom.ref. exists)
1256 if (nomRef
!= null && nomRef
.getAuthorship() != null){
1257 if(authorMatcher
.invoke(name
.getCombinationAuthorship(), nomRef
.getAuthorship()).isSuccessful()){
1258 name
.setCombinationAuthorship(nomRef
.getAuthorship());
1261 name
.setCombinationAuthorship(deduplicateAuthor(name
.getCombinationAuthorship()));
1263 if (name
.getExCombinationAuthorship()!= null && !name
.getExCombinationAuthorship().isPersited()){
1264 name
.setExCombinationAuthorship(deduplicateAuthor(name
.getExCombinationAuthorship()));
1266 if (name
.getBasionymAuthorship()!= null && !name
.getBasionymAuthorship().isPersited()){
1267 name
.setBasionymAuthorship(deduplicateAuthor(name
.getBasionymAuthorship()));
1269 if (name
.getExBasionymAuthorship()!= null && !name
.getExBasionymAuthorship().isPersited()){
1270 name
.setExBasionymAuthorship(deduplicateAuthor(name
.getExBasionymAuthorship()));
1274 if (name
.getOriginalSpelling()!= null && !name
.getOriginalSpelling().isPersited()){
1275 TaxonName origName
= name
.getOriginalSpelling();
1276 IMatchStrategy nameMatcher
= MatchStrategyFactory
.NewParsedOriginalSpellingInstance();
1277 List
<TaxonName
> matchingNames
= commonService
.findMatching(origName
, nameMatcher
);
1278 if(matchingNames
.size() >= 1){
1279 TaxonName duplicate
= findBestMatching(origName
, matchingNames
, nameMatcher
);
1280 name
.setOriginalSpelling(duplicate
);
1284 // Logger.getLogger("org.hibernate.SQL").setLevel(sqlLogLevel);
1285 } catch (MatchException e
) {
1286 throw new RuntimeException(e
);
1289 result
.setCdmEntity(name
);
1293 private TeamOrPersonBase
<?
> deduplicateAuthor(TeamOrPersonBase
<?
> authorship
) throws MatchException
{
1294 if (authorship
== null){
1297 IParsedMatchStrategy authorMatcher
= MatchStrategyFactory
.NewParsedTeamOrPersonInstance();
1298 List
<TeamOrPersonBase
<?
>> matchingAuthors
= commonService
.findMatching(authorship
, authorMatcher
);
1299 if(matchingAuthors
.size() >= 1){
1300 TeamOrPersonBase
<?
> duplicate
= findBestMatching(authorship
, matchingAuthors
, authorMatcher
);
1303 if (authorship
instanceof Team
){
1304 deduplicateTeam((Team
)authorship
);
1310 private void deduplicateTeam(Team team
) throws MatchException
{
1311 List
<Person
> members
= team
.getTeamMembers();
1312 IParsedMatchStrategy personMatcher
= MatchStrategyFactory
.NewParsedPersonInstance();
1313 for (int i
=0; i
< members
.size(); i
++){
1314 Person person
= CdmBase
.deproxy(members
.get(i
));
1315 List
<Person
> matchingPersons
= commonService
.findMatching(person
, personMatcher
);
1316 if (matchingPersons
.size() > 0){
1317 person
= findBestMatching(person
, matchingPersons
, personMatcher
);
1318 members
.set(i
, person
);
1323 private <M
extends IMatchable
> M
findBestMatching(M matchable
, List
<M
> matchingList
,
1324 IMatchStrategy matcher
) {
1325 // FIXME TODO resolve multiple duplications. Use first match for a start
1326 if(matchingList
.isEmpty()){
1329 M bestMatching
= matchingList
.iterator().next();
1330 return bestMatching
;