3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
11 package eu
.etaxonomy
.cdm
.api
.service
;
13 import java
.io
.IOException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.EnumSet
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
22 import java
.util
.UUID
;
24 import javax
.persistence
.EntityNotFoundException
;
26 import org
.apache
.log4j
.Logger
;
27 import org
.apache
.lucene
.index
.CorruptIndexException
;
28 import org
.apache
.lucene
.queryParser
.ParseException
;
29 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
30 import org
.apache
.lucene
.search
.BooleanFilter
;
31 import org
.apache
.lucene
.search
.BooleanQuery
;
32 import org
.apache
.lucene
.search
.DocIdSet
;
33 import org
.apache
.lucene
.search
.Query
;
34 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
35 import org
.apache
.lucene
.search
.SortField
;
36 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
37 import org
.springframework
.stereotype
.Service
;
38 import org
.springframework
.transaction
.annotation
.Transactional
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
41 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
42 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.SynonymDeletionConfigurator
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
46 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
47 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
48 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
49 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
50 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
60 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
61 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
62 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
63 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
64 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
65 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
66 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
67 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
68 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
69 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
70 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
71 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
72 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
73 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
74 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
75 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
76 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
77 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
78 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
79 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
80 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
81 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
82 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
83 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
84 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
85 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
86 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
87 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
88 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
89 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
90 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
91 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
92 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
93 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
94 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
95 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
96 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
97 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
98 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
99 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
100 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
101 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
102 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
103 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
104 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
105 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
106 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
108 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
115 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
116 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
117 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
118 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
119 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
122 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
123 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
124 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
125 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
126 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
130 * @author a.kohlbecker
135 @Transactional(readOnly
= true)
136 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
137 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
139 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
141 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
143 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
147 private ITaxonNameDao nameDao
;
150 private INameService nameService
;
153 private ITaxonNodeService nodeService
;
156 private ICdmGenericDao genericDao
;
159 private IDescriptionService descriptionService
;
162 private IOrderedTermVocabularyDao orderedVocabularyDao
;
165 private IOccurrenceDao occurrenceDao
;
168 private IClassificationDao classificationDao
;
171 private AbstractBeanInitializer beanInitializer
;
174 private ILuceneIndexToolProvider luceneIndexToolProvider
;
179 public TaxonServiceImpl(){
180 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
184 * FIXME Candidate for harmonization
185 * rename searchByName ?
188 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
189 return dao
.getTaxaByName(name
, sec
);
193 * FIXME Candidate for harmonization
194 * merge with getRootTaxa(Reference sec, ..., ...)
196 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
199 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
200 if (cdmFetch
== null){
201 cdmFetch
= CdmFetch
.NO_FETCH();
203 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
207 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
208 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
212 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
213 return dao
.getAllRelationships(limit
, start
);
217 * FIXME Candidate for harmonization
218 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
222 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
224 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
225 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
226 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
227 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
228 return taxonRelTypeVocabulary
;
235 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
238 @Transactional(readOnly
= false)
239 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
241 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
242 synonymName
.removeTaxonBase(synonym
);
243 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
244 taxonName
.removeTaxonBase(acceptedTaxon
);
246 synonym
.setName(taxonName
);
247 acceptedTaxon
.setName(synonymName
);
249 // the accepted taxon needs a new uuid because the concept has changed
250 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
251 //acceptedTaxon.setUuid(UUID.randomUUID());
256 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
260 @Transactional(readOnly
= false)
261 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
263 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
264 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
265 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
267 //check synonym is not homotypic
268 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
269 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
270 throw new HomotypicalGroupChangeException(message
);
273 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
275 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
276 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
277 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
279 for (Synonym heteroSynonym
: heteroSynonyms
){
280 if (synonym
.equals(heteroSynonym
)){
281 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
284 //move synonyms in same homotypic group to new accepted taxon
285 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
289 //synonym.getName().removeTaxonBase(synonym);
292 // deleteSynonym(synonym, taxon, false);
295 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
296 config
.setDeleteNameIfPossible(false);
297 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
299 } catch (Exception e
) {
300 logger
.info("Can't delete old synonym from database");
304 return newAcceptedTaxon
;
309 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
311 // Get name from synonym
312 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
314 /* // remove synonym from taxon
315 toTaxon.removeSynonym(synonym);
317 // Create a taxon with synonym name
318 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
320 // Add taxon relation
321 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
323 // since we are swapping names, we have to detach the name from the synonym completely.
324 // Otherwise the synonym will still be in the list of typified names.
325 // synonym.getName().removeTaxonBase(synonym);
326 this.deleteSynonym(synonym
, null);
333 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeHomotypicalGroupOfSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.name.HomotypicalGroup, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
335 @Transactional(readOnly
= false)
337 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
338 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
340 TaxonNameBase synonymName
= synonym
.getName();
341 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
345 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
346 newHomotypicalGroup
.addTypifiedName(synonymName
);
348 //remove existing basionym relationships
349 synonymName
.removeBasionyms();
351 //add basionym relationship
352 if (setBasionymRelationIfApplicable
){
353 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
354 for (TaxonNameBase basionym
: basionyms
){
355 synonymName
.addBasionym(basionym
);
359 //set synonym relationship correctly
360 // SynonymRelationship relToTaxon = null;
361 boolean relToTargetTaxonExists
= false;
362 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
363 for (SynonymRelationship rel
: existingRelations
){
364 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
365 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
366 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
367 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
368 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
369 rel
.setType(newRelationType
);
370 //TODO handle citation and microCitation
373 relToTargetTaxonExists
= true;
375 if (removeFromOtherTaxa
){
376 acceptedTaxon
.removeSynonym(synonym
, false);
382 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
383 Taxon acceptedTaxon
= targetTaxon
;
384 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
385 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
386 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
387 //TODO handle citation and microCitation
388 Reference citation
= null;
389 String microCitation
= null;
390 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
397 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
400 @Transactional(readOnly
= false)
401 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
403 clazz
= TaxonBase
.class;
405 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
410 protected void setDao(ITaxonDao dao
) {
415 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
418 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
419 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
421 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
422 if(numberOfResults
> 0) { // no point checking again
423 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
426 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
430 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
433 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
434 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
436 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
437 if(numberOfResults
> 0) { // no point checking again
438 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
445 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
448 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
449 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
451 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
452 if(numberOfResults
> 0) { // no point checking again
453 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
459 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
462 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
463 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
465 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
466 if(numberOfResults
> 0) { // no point checking again
467 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
469 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
473 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
476 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
477 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
479 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
480 if(numberOfResults
> 0) { // no point checking again
481 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
487 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
490 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
491 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
493 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
494 if(numberOfResults
> 0) { // no point checking again
495 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
497 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
501 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
502 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
503 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
507 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
508 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
510 List
<Taxon
> list
= new ArrayList
<Taxon
>();
513 Synonym synonym
= null;
516 synonym
= (Synonym
) dao
.load(synonymUuid
);
517 } catch (ClassCastException e
){
518 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
519 } catch (NullPointerException e
){
520 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
523 Classification classificationFilter
= null;
524 if(classificationUuid
!= null){
526 classificationFilter
= classificationDao
.load(classificationUuid
);
527 } catch (NullPointerException e
){
528 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
530 if(classificationFilter
== null){
535 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
536 if(count
> (pageSize
* pageNumber
)){
537 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
540 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
546 * @param includeRelationships
550 * @param propertyPaths
551 * @return an List which is not specifically ordered
554 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
555 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
557 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
558 relatedTaxa
.remove(taxon
);
559 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
565 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
566 * <code>taxon</code> supplied as parameter.
569 * @param includeRelationships
571 * @param maxDepth can be <code>null</code> for infinite depth
574 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
580 if(includeRelationships
.isEmpty()){
584 if(maxDepth
!= null) {
587 if(logger
.isDebugEnabled()){
588 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
590 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
591 for (TaxonRelationship taxRel
: taxonRelationships
) {
594 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
597 // filter by includeRelationships
598 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
599 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
600 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
601 if(logger
.isDebugEnabled()){
602 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
604 taxa
.add(taxRel
.getToTaxon());
605 if(maxDepth
== null || maxDepth
> 0) {
606 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
609 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
610 taxa
.add(taxRel
.getFromTaxon());
611 if(logger
.isDebugEnabled()){
612 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
614 if(maxDepth
== null || maxDepth
> 0) {
615 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
625 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
628 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
629 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
631 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
632 if(numberOfResults
> 0) { // no point checking again
633 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
636 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
640 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
643 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
644 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
646 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
647 if(numberOfResults
> 0) { // no point checking again
648 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
651 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
655 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
658 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
659 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
660 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
663 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
666 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
667 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
668 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
676 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
679 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
680 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
681 return t
.getHomotypicSynonymsByHomotypicGroup();
685 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
688 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
689 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
690 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
691 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
692 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
693 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
695 return heterotypicSynonymyGroups
;
699 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
701 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
702 // Class<? extends TaxonBase> clazz = null;
703 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
704 // clazz = TaxonBase.class;
705 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
706 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
707 // } else if(configurator.isDoTaxa()) {
708 // clazz = Taxon.class;
709 // //propertyPath = configurator.getTaxonPropertyPath();
710 // } else if (configurator.isDoSynonyms()) {
711 // clazz = Synonym.class;
712 // //propertyPath = configurator.getSynonymPropertyPath();
716 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
721 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
724 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
726 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
727 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
728 List
<TaxonBase
> taxa
= null;
731 long numberTaxaResults
= 0L;
734 List
<String
> propertyPath
= new ArrayList
<String
>();
735 if(configurator
.getTaxonPropertyPath() != null){
736 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
740 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
741 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
743 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
744 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
745 configurator
.getNamedAreas());
748 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
749 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
750 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
751 configurator
.getMatchMode(), configurator
.getNamedAreas(),
752 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
756 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
759 results
.addAll(taxa
);
762 numberOfResults
+= numberTaxaResults
;
764 // Names without taxa
765 if (configurator
.isDoNamesWithoutTaxa()) {
766 int numberNameResults
= 0;
768 List
<?
extends TaxonNameBase
<?
,?
>> names
=
769 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
770 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
771 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
772 if (names
.size() > 0) {
773 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
774 if (taxonName
.getTaxonBases().size() == 0) {
775 results
.add(taxonName
);
779 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
780 numberOfResults
+= numberNameResults
;
784 // Taxa from common names
786 if (configurator
.isDoTaxaByCommonNames()) {
787 taxa
= new ArrayList
<TaxonBase
>();
788 numberTaxaResults
= 0;
789 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
790 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
792 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
793 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
794 for( Object
[] entry
: commonNameResults
) {
795 taxa
.add((TaxonBase
) entry
[0]);
799 results
.addAll(taxa
);
801 numberOfResults
+= numberTaxaResults
;
805 return new DefaultPagerImpl
<IdentifiableEntity
>
806 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
809 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
810 return dao
.getUuidAndTitleCache();
814 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
817 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
818 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
819 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
820 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
821 for (TaxonDescription taxDesc
: descriptions
){
822 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
823 for (DescriptionElementBase descElem
: elements
){
824 for(Media media
: descElem
.getMedia()){
826 //find the best matching representation
827 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
836 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
839 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
840 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
845 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
848 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
849 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
850 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
852 logger
.trace("listMedia() - START");
854 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
855 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
857 if (limitToGalleries
== null) {
858 limitToGalleries
= false;
861 // --- resolve related taxa
862 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
863 logger
.trace("listMedia() - resolve related taxa");
864 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
867 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
869 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
870 logger
.trace("listMedia() - includeTaxonDescriptions");
871 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
872 // --- TaxonDescriptions
873 for (Taxon t
: taxa
) {
874 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
876 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
877 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
878 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
879 for (Media media
: element
.getMedia()) {
880 taxonMedia
.add(media
);
888 if(includeOccurrences
!= null && includeOccurrences
) {
889 logger
.trace("listMedia() - includeOccurrences");
890 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
892 for (Taxon t
: taxa
) {
893 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
895 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
897 // direct media removed from specimen #3597
898 // taxonMedia.addAll(occurrence.getMedia());
900 // SpecimenDescriptions
901 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
902 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
903 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
904 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
905 for (DescriptionElementBase element
: elements
) {
906 for (Media media
: element
.getMedia()) {
907 taxonMedia
.add(media
);
914 //TODO why may collections have media attached? #
915 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
916 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
917 if (derivedUnit
.getCollection() != null){
918 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
922 // pherograms & gelPhotos
923 if (occurrence
.isInstanceOf(DnaSample
.class)) {
924 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
925 Set
<Sequence
> sequences
= dnaSample
.getSequences();
926 //we do show only those gelPhotos which lead to a consensus sequence
927 for (Sequence sequence
: sequences
) {
928 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
929 for (SingleRead singleRead
: sequence
.getSingleReads()){
930 Amplification amplification
= singleRead
.getAmplification();
931 dnaRelatedMedia
.add(amplification
.getGelPhoto());
932 dnaRelatedMedia
.add(singleRead
.getPherogram());
933 dnaRelatedMedia
.remove(null);
935 taxonMedia
.addAll(dnaRelatedMedia
);
942 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
943 logger
.trace("listMedia() - includeTaxonNameDescriptions");
944 // --- TaxonNameDescription
945 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
946 for (Taxon t
: taxa
) {
947 nameDescriptions
.addAll(t
.getName().getDescriptions());
949 for(TaxonNameDescription nameDescription
: nameDescriptions
){
950 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
951 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
952 for (DescriptionElementBase element
: elements
) {
953 for (Media media
: element
.getMedia()) {
954 taxonMedia
.add(media
);
962 logger
.trace("listMedia() - initialize");
963 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
965 logger
.trace("listMedia() - END");
971 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
974 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
975 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
979 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
982 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
983 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
987 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
990 public int countAllRelationships() {
991 return this.dao
.countAllRelationships();
998 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
1001 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
1002 return this.dao
.findIdenticalTaxonNames(propertyPath
);
1007 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1010 public String
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
1011 if (config
== null){
1012 config
= new TaxonDeletionConfigurator();
1015 List
<String
> referencedObjects
= isDeletable(taxon
, config
);
1017 if (referencedObjects
.isEmpty()){
1018 // --- DeleteSynonymRelations
1019 if (config
.isDeleteSynonymRelations()){
1020 boolean removeSynonymNameFromHomotypicalGroup
= false;
1021 // use tmp Set to avoid concurrent modification
1022 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1023 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1024 for (SynonymRelationship synRel
: synRelsToDelete
){
1025 Synonym synonym
= synRel
.getSynonym();
1026 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1027 // this will cause hibernate to delete the relationship since
1028 // the SynonymRelationship field on both is annotated with removeOrphan
1029 // so no further explicit deleting of the relationship should be done here
1030 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1032 // --- DeleteSynonymsIfPossible
1033 if (config
.isDeleteSynonymsIfPossible()){
1035 boolean newHomotypicGroupIfNeeded
= true;
1036 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1037 deleteSynonym(synonym
, taxon
, synConfig
);
1039 // relationship will be deleted by hibernate automatically,
1040 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1042 // deleteSynonymRelationships(synonym, taxon);
1047 // --- DeleteTaxonRelationships
1048 if (! config
.isDeleteTaxonRelationships()){
1049 if (taxon
.getTaxonRelations().size() > 0){
1050 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1051 "Remove taxon from all relations to other taxa prior to deletion.";
1052 // throw new ReferencedObjectUndeletableException(message);
1055 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1056 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1057 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1058 if (taxon
.equals(taxRel
.getToTaxon())){
1059 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1063 taxon
.removeTaxonRelation(taxRel
);
1064 /*if (taxFrom.equals(taxon)){
1066 this.deleteTaxon(taxTo, taxConf, classification);
1067 } catch(DataChangeNoRollbackException e){
1068 logger.debug("A related taxon will not be deleted." + e.getMessage());
1072 this.deleteTaxon(taxFrom, taxConf, classification);
1073 } catch(DataChangeNoRollbackException e){
1074 logger.debug("A related taxon will not be deleted." + e.getMessage());
1082 if (config
.isDeleteDescriptions()){
1083 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1084 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1085 for (TaxonDescription desc
: descriptions
){
1086 //TODO use description delete configurator ?
1087 //FIXME check if description is ALWAYS deletable
1088 if (desc
.getDescribedSpecimenOrObservation() != null){
1089 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1090 " which also describes specimens or abservations";
1091 //throw new ReferencedObjectUndeletableException(message);
1093 removeDescriptions
.add(desc
);
1094 descriptionService
.delete(desc
);
1097 for (TaxonDescription desc
: removeDescriptions
){
1098 taxon
.removeDescription(desc
);
1103 /* //check references with only reverse mapping
1104 String message = checkForReferences(taxon);
1105 if (message != null){
1106 //throw new ReferencedObjectUndeletableException(message.toString());
1109 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1110 //if (taxon.getTaxonNodes().size() > 0){
1111 // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";
1112 // throw new ReferencedObjectUndeletableException(message);
1115 if (taxon
.getTaxonNodes().size() != 0){
1116 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1117 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1118 TaxonNode node
= null;
1119 boolean deleteChildren
;
1120 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1121 deleteChildren
= true;
1123 deleteChildren
= false;
1125 boolean success
= true;
1126 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1127 while (iterator
.hasNext()){
1128 node
= iterator
.next();
1129 if (node
.getClassification().equals(classification
)){
1135 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1136 nodeService
.delete(node
);
1138 // message = "Taxon is not used in defined classification";
1139 // throw new DataChangeNoRollbackException(message);
1141 } else if (config
.isDeleteInAllClassifications()){
1142 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1143 nodesList
.addAll(taxon
.getTaxonNodes());
1145 for (ITaxonTreeNode treeNode
: nodesList
){
1146 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1147 if(!deleteChildren
){
1148 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1149 //nodesList.addAll(taxonNode.getChildNodes());
1150 for (Object childNode: childNodes){
1151 TaxonNode childNodeCast = (TaxonNode) childNode;
1152 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1156 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1157 deleteTaxon(childNode.getTaxon(), config, classification);
1160 // taxon.removeTaxonNode(taxonNode);
1161 //nodeService.delete(taxonNode);
1164 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1165 for (Object childNode
: childNodes
){
1166 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1167 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1170 //taxon.removeTaxonNode(taxonNode);
1173 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1174 nodeService
.deleteTaxonNodes(nodesList
, config
);
1177 // message = "The taxon node could not be deleted.";
1178 //throw new DataChangeNoRollbackException(message);
1184 //PolytomousKey TODO
1186 boolean usedInPolytomousKey
= checkForPolytomousKeys(taxon
);
1188 if (config
.isDeleteNameIfPossible()){
1191 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1192 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1193 //check whether taxon will be deleted or not
1194 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1195 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1196 name
.removeTaxonBase(taxon
);
1197 nameService
.save(name
);
1198 String uuidString
= nameService
.delete(name
, config
.getNameDeletionConfig());
1199 logger
.debug(uuidString
);
1205 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1207 for (TaxonDescription desc: descriptions){
1208 if (config.isDeleteDescriptions()){
1209 //TODO use description delete configurator ?
1210 //FIXME check if description is ALWAYS deletable
1211 taxon.removeDescription(desc);
1212 descriptionService.delete(desc);
1214 if (desc.getDescribedSpecimenOrObservations().size()>0){
1215 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1216 " which also describes specimens or observations";
1217 throw new ReferencedObjectUndeletableException(message);
1222 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1223 UUID uuid
= dao
.delete(taxon
);
1224 return uuid
.toString();
1226 return "The Taxon can't be deleted.";
1229 return referencedObjects
.toString();
1235 private String
checkForReferences(Taxon taxon
){
1236 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1237 for (CdmBase referencingObject
: referencingObjects
){
1238 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1239 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1240 String message
= "Taxon" + taxon
.getTitleCache() + "can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
1246 /* //PolytomousKeyNode
1247 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1248 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1253 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1254 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1259 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1260 String message
= "Taxon can't be deleted as it is used in a determination event";
1266 referencingObjects
= null;
1270 private boolean checkForPolytomousKeys(Taxon taxon
){
1271 boolean result
= false;
1272 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1273 if (!list
.isEmpty()) {
1279 @Transactional(readOnly
= false)
1280 public UUID
delete(Synonym syn
){
1281 UUID result
= syn
.getUuid();
1282 this.deleteSynonym(syn
, null);
1287 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1289 @Transactional(readOnly
= false)
1291 public String
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1292 return deleteSynonym(synonym
, null, config
);
1298 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1300 @Transactional(readOnly
= false)
1302 public String
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1303 if (synonym
== null){
1307 if (config
== null){
1308 config
= new SynonymDeletionConfigurator();
1310 List
<String
> messages
= isDeletable(synonym
, config
);
1311 if (messages
.isEmpty()){
1312 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1314 //remove synonymRelationship
1315 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1317 taxonSet
.add(taxon
);
1319 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1321 for (Taxon relatedTaxon
: taxonSet
){
1322 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1323 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1325 this.saveOrUpdate(synonym
);
1327 //TODO remove name from homotypical group?
1329 //remove synonym (if necessary)
1332 if (synonym
.getSynonymRelations().isEmpty()){
1333 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1334 synonym
.setName(null);
1335 uuid
= dao
.delete(synonym
);
1337 //remove name if possible (and required)
1338 if (name
!= null && config
.isDeleteNameIfPossible()){
1340 nameService
.delete(name
, config
.getNameDeletionConfig());
1347 return uuid
.toString();
1349 return messages
.toString();
1357 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1360 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1362 return this.dao
.findIdenticalNamesNew(propertyPath
);
1366 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1369 public String
getPhylumName(TaxonNameBase name
){
1370 return this.dao
.getPhylumName(name
);
1374 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1377 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1378 return dao
.deleteSynonymRelationships(syn
, taxon
);
1382 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1385 public long deleteSynonymRelationships(Synonym syn
) {
1386 return dao
.deleteSynonymRelationships(syn
, null);
1391 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listSynonymRelationships(eu.etaxonomy.cdm.model.taxon.TaxonBase, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List, eu.etaxonomy.cdm.model.common.RelationshipBase.Direction)
1394 public List
<SynonymRelationship
> listSynonymRelationships(
1395 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1396 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1397 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1399 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1400 if(numberOfResults
> 0) { // no point checking again
1401 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1407 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1410 public Taxon
findBestMatchingTaxon(String taxonName
) {
1411 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1412 config
.setTaxonNameTitle(taxonName
);
1413 return findBestMatchingTaxon(config
);
1419 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1421 Taxon bestCandidate
= null;
1423 // 1. search for acceptet taxa
1424 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1425 boolean bestCandidateMatchesSecUuid
= false;
1426 boolean bestCandidateIsInClassification
= false;
1427 int countEqualCandidates
= 0;
1428 for(TaxonBase taxonBaseCandidate
: taxonList
){
1429 if(taxonBaseCandidate
instanceof Taxon
){
1430 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1431 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1432 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1434 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1435 bestCandidate
= newCanditate
;
1436 countEqualCandidates
= 1;
1437 bestCandidateMatchesSecUuid
= true;
1441 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1442 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1444 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1445 bestCandidate
= newCanditate
;
1446 countEqualCandidates
= 1;
1447 bestCandidateIsInClassification
= true;
1450 if (bestCandidate
== null){
1451 bestCandidate
= newCanditate
;
1452 countEqualCandidates
= 1;
1456 }else{ //not Taxon.class
1459 countEqualCandidates
++;
1462 if (bestCandidate
!= null){
1463 if(countEqualCandidates
> 1){
1464 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1465 return bestCandidate
;
1467 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1468 return bestCandidate
;
1473 // 2. search for synonyms
1474 if (config
.isIncludeSynonyms()){
1475 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1476 for(TaxonBase taxonBase
: synonymList
){
1477 if(taxonBase
instanceof Synonym
){
1478 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1479 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1480 if(!acceptetdCandidates
.isEmpty()){
1481 bestCandidate
= acceptetdCandidates
.iterator().next();
1482 if(acceptetdCandidates
.size() == 1){
1483 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1484 return bestCandidate
;
1486 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1487 return bestCandidate
;
1489 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1495 } catch (Exception e
){
1497 e
.printStackTrace();
1500 return bestCandidate
;
1503 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1504 UUID configClassificationUuid
= config
.getClassificationUuid();
1505 if (configClassificationUuid
== null){
1508 for (TaxonNode node
: taxon
.getTaxonNodes()){
1509 UUID classUuid
= node
.getClassification().getUuid();
1510 if (configClassificationUuid
.equals(classUuid
)){
1517 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1518 UUID configSecUuid
= config
.getSecUuid();
1519 if (configSecUuid
== null){
1522 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1523 return configSecUuid
.equals(taxonSecUuid
);
1527 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1530 public Synonym
findBestMatchingSynonym(String taxonName
) {
1531 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1532 if(! synonymList
.isEmpty()){
1533 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1534 if(synonymList
.size() == 1){
1535 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1538 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1547 * @see eu.etaxonomy.cdm.api.service.ITaxonService#moveSynonymToAnotherTaxon(eu.etaxonomy.cdm.model.taxon.SynonymRelationship, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String, boolean)
1550 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1551 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1553 Synonym synonym
= oldSynonymRelation
.getSynonym();
1554 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1555 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1556 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1557 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1558 //set default relationship type
1559 if (newSynonymRelationshipType
== null){
1560 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1562 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1564 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1565 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1566 boolean isSingleInGroup
= !(hgSize
> 1);
1568 if (! isSingleInGroup
){
1569 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1570 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1571 if (isHomotypicToAccepted
){
1572 String message
= "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";
1573 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1574 message
= String
.format(message
, homotypicRelatives
);
1575 throw new HomotypicalGroupChangeException(message
);
1577 if (! moveHomotypicGroup
){
1578 String message
= "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";
1579 throw new HomotypicalGroupChangeException(message
);
1582 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1584 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1586 SynonymRelationship result
= null;
1587 //move all synonyms to new taxon
1588 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1589 for (Synonym syn
: homotypicSynonyms
){
1590 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1591 for (SynonymRelationship synRelation
: synRelations
){
1592 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1593 Reference
<?
> newReference
= reference
;
1594 if (newReference
== null && keepReference
){
1595 newReference
= synRelation
.getCitation();
1597 String newRefDetail
= referenceDetail
;
1598 if (newRefDetail
== null && keepReference
){
1599 newRefDetail
= synRelation
.getCitationMicroReference();
1601 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1602 fromTaxon
.removeSynonymRelation(synRelation
, false);
1604 //change homotypic group of synonym if relType is 'homotypic'
1605 // if (newRelTypeIsHomotypic){
1606 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1609 if (synRelation
.equals(oldSynonymRelation
)){
1610 result
= newSynRelation
;
1616 saveOrUpdate(newTaxon
);
1617 //Assert that there is a result
1618 if (result
== null){
1619 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1620 throw new IllegalStateException(message
);
1626 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1629 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1630 return dao
.getUuidAndTitleCacheTaxon();
1634 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1637 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1638 return dao
.getUuidAndTitleCacheSynonym();
1642 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
1645 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1646 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1647 Classification classification
, List
<Language
> languages
,
1648 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1651 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1653 // --- execute search
1654 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1656 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1657 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1659 // --- initialize taxa, thighlight matches ....
1660 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1661 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1662 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1664 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1665 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1669 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1670 Classification classification
,
1671 Integer pageSize
, Integer pageNumber
,
1672 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1674 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1676 // --- execute search
1677 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1679 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1680 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1682 // --- initialize taxa, thighlight matches ....
1683 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1684 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1685 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1687 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1688 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1693 * @param queryString
1694 * @param classification
1696 * @param highlightFragments
1697 * @param sortFields TODO
1698 * @param directorySelectClass
1701 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1702 boolean highlightFragments
, SortField
[] sortFields
) {
1703 BooleanQuery finalQuery
= new BooleanQuery();
1704 BooleanQuery textQuery
= new BooleanQuery();
1706 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1707 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1709 if(sortFields
== null){
1710 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1712 luceneSearch
.setSortFields(sortFields
);
1714 // ---- search criteria
1715 luceneSearch
.setCdmTypRestriction(clazz
);
1717 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1718 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1720 finalQuery
.add(textQuery
, Occur
.MUST
);
1722 if(classification
!= null){
1723 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1725 luceneSearch
.setQuery(finalQuery
);
1727 if(highlightFragments
){
1728 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1730 return luceneSearch
;
1734 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1735 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1736 * drawback of requiring to do the join an indexing time.
1737 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1739 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1741 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1742 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1744 * @param queryString
1745 * @param classification
1747 * @param highlightFragments
1748 * @param sortFields TODO
1751 * @throws IOException
1753 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1754 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1757 String queryTermField
;
1758 String toField
= "id"; // TaxonBase.uuid
1760 if(edge
.isBidirectional()){
1761 throw new RuntimeException("Bidirectional joining not supported!");
1764 fromField
= "relatedFrom.id";
1765 queryTermField
= "relatedFrom.titleCache";
1766 } else if(edge
.isInvers()) {
1767 fromField
= "relatedTo.id";
1768 queryTermField
= "relatedTo.titleCache";
1770 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1773 BooleanQuery finalQuery
= new BooleanQuery();
1775 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1776 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1778 BooleanQuery joinFromQuery
= new BooleanQuery();
1779 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1780 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1781 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1783 if(sortFields
== null){
1784 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1786 luceneSearch
.setSortFields(sortFields
);
1788 finalQuery
.add(joinQuery
, Occur
.MUST
);
1790 if(classification
!= null){
1791 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1793 luceneSearch
.setQuery(finalQuery
);
1795 if(highlightFragments
){
1796 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1798 return luceneSearch
;
1805 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNamesByFullText(java.util.EnumSet, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.Set, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.Map)
1808 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1809 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1810 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1811 boolean highlightFragments
, Integer pageSize
,
1812 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1813 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1815 // FIXME: allow taxonomic ordering
1816 // hql equivalent: order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
1817 // this require building a special sort column by a special classBridge
1818 if(highlightFragments
){
1819 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1820 "currently not fully supported by this method and thus " +
1821 "may not work with common names and misapplied names.");
1824 // convert sets to lists
1825 List
<NamedArea
> namedAreaList
= null;
1826 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1827 if(namedAreas
!= null){
1828 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1829 namedAreaList
.addAll(namedAreas
);
1831 if(distributionStatus
!= null){
1832 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1833 distributionStatusList
.addAll(distributionStatus
);
1836 // set default if parameter is null
1837 if(searchModes
== null){
1838 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1841 // set sort order and thus override any sort orders which may have been
1842 // defindes by prepare*Search methods
1843 if(orderHints
== null){
1844 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1846 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1848 for(OrderHint oh
: orderHints
){
1849 sortFields
[i
++] = oh
.toSortField();
1851 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1852 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1855 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1857 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1858 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1861 ======== filtering by distribution , HOWTO ========
1863 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1864 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1865 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1866 which will be put into a FilteredQuersy in the end ?
1869 3. how does it work in spatial?
1871 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1872 - http://www.infoq.com/articles/LuceneSpatialSupport
1873 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1874 ------------------------------------------------------------------------
1877 A) use a separate distribution filter per index sub-query/search:
1878 - byTaxonSyonym (query TaxaonBase):
1879 use a join area filter (Distribution -> TaxonBase)
1880 - byCommonName (query DescriptionElementBase): use an area filter on
1881 DescriptionElementBase !!! PROBLEM !!!
1882 This cannot work since the distributions are different entities than the
1883 common names and thus these are different lucene documents.
1884 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1885 use a join area filter (Distribution -> TaxonBase)
1887 B) use a common distribution filter for all index sub-query/searches:
1888 - use a common join area filter (Distribution -> TaxonBase)
1889 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1890 PROBLEM in this case: we are losing the fragment highlighting for the
1891 common names, since the returned documents are always TaxonBases
1894 /* The QueryFactory for creating filter queries on Distributions should
1895 * The query factory used for the common names query cannot be reused
1896 * for this case, since we want to only record the text fields which are
1897 * actually used in the primary query
1899 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1901 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1904 // search for taxa or synonyms
1905 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1906 Class taxonBaseSubclass
= TaxonBase
.class;
1907 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1908 taxonBaseSubclass
= Taxon
.class;
1909 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1910 taxonBaseSubclass
= Synonym
.class;
1912 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1913 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1914 /* A) does not work!!!!
1915 if(addDistributionFilter){
1916 // in this case we need a filter which uses a join query
1917 // to get the TaxonBase documents for the DescriptionElementBase documents
1918 // which are matching the areas in question
1919 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1921 distributionStatusList,
1922 distributionFilterQueryFactory
1924 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1927 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1928 // add additional area filter for synonyms
1929 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1930 String toField
= "accTaxon.id"; // id in TaxonBase index
1932 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1934 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1935 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1940 // search by CommonTaxonName
1941 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1943 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1944 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1945 "inDescription.taxon.id",
1947 QueryFactory
.addTypeRestriction(
1948 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1949 , CommonTaxonName
.class
1951 CommonTaxonName
.class);
1952 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1953 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1954 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1955 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1956 byCommonNameSearch
.setSortFields(sortFields
);
1957 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1959 luceneSearches
.add(byCommonNameSearch
);
1961 /* A) does not work!!!!
1963 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1964 queryString, classification, null, languages, highlightFragments)
1966 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1967 if(addDistributionFilter){
1968 // in this case we are able to use DescriptionElementBase documents
1969 // which are matching the areas in question directly
1970 BooleanQuery byDistributionQuery = createByDistributionQuery(
1972 distributionStatusList,
1973 distributionFilterQueryFactory
1975 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1979 // search by misapplied names
1980 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1982 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1983 // which allows doing query time joins
1984 // finds the misapplied name (Taxon B) which is an misapplication for
1985 // a related Taxon A.
1987 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1988 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1989 queryString
, classification
, languages
, highlightFragments
, sortFields
));
1990 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1992 if(addDistributionFilter
){
1993 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1996 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1997 * Maybe this is a but in java itself java.
1999 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2002 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2004 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2005 * will execute as expected:
2007 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2008 * String toField = "relation." + misappliedNameForUuid +".to.id";
2010 * Comparing both strings by the String.equals method returns true, so both String are identical.
2012 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2013 * dependent from a specific jvm (openjdk6 6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2, oracle jdk1.7.0_25 tested)
2014 * The bug is persistent after a reboot of the development computer.
2016 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2017 // String toField = "relation." + misappliedNameForUuid +".to.id";
2018 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2019 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2020 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2022 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2023 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2024 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2026 // debug code for bug described above
2027 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2028 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2030 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2034 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2035 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2038 if(addDistributionFilter
){
2041 // in this case we need a filter which uses a join query
2042 // to get the TaxonBase documents for the DescriptionElementBase documents
2043 // which are matching the areas in question
2045 // for toTaxa, doByCommonName
2046 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2048 distributionStatusList
,
2049 distributionFilterQueryFactory
2051 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2054 if (addDistributionFilter
){
2055 multiSearch
.setFilter(multiIndexByAreaFilter
);
2059 // --- execute search
2060 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2062 // --- initialize taxa, highlight matches ....
2063 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2066 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2067 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2069 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2070 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2074 * @param namedAreaList at least one area must be in the list
2075 * @param distributionStatusList optional
2077 * @throws IOException
2079 protected Query
createByDistributionJoinQuery(
2080 List
<NamedArea
> namedAreaList
,
2081 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2082 QueryFactory queryFactory
2083 ) throws IOException
{
2085 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2086 String toField
= "id"; // id in TaxonBase index
2088 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2090 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2092 return taxonAreaJoinQuery
;
2096 * @param namedAreaList
2097 * @param distributionStatusList
2098 * @param queryFactory
2101 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2102 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
2103 BooleanQuery areaQuery
= new BooleanQuery();
2104 // area field from Distribution
2105 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2107 // status field from Distribution
2108 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2109 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2112 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2117 * This method has been primarily created for testing the area join query but might
2118 * also be useful in other situations
2120 * @param namedAreaList
2121 * @param distributionStatusList
2122 * @param classification
2123 * @param highlightFragments
2125 * @throws IOException
2127 protected LuceneSearch
prepareByDistributionSearch(
2128 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2129 Classification classification
) throws IOException
{
2131 BooleanQuery finalQuery
= new BooleanQuery();
2133 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2135 // FIXME is this query factory using the wrong type?
2136 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2138 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2139 luceneSearch
.setSortFields(sortFields
);
2142 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2144 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2146 if(classification
!= null){
2147 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2150 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2151 luceneSearch
.setQuery(finalQuery
);
2153 return luceneSearch
;
2159 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
2162 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2163 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2164 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2165 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2168 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2170 // --- execute search
2171 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2173 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2174 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2176 // --- initialize taxa, highlight matches ....
2177 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2178 @SuppressWarnings("rawtypes")
2179 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2180 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2182 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2183 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2189 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2190 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2191 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2193 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2194 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2196 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2198 // --- execute search
2199 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2201 // --- initialize taxa, highlight matches ....
2202 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2204 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2205 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2206 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2208 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2209 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2211 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2212 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2219 * @param queryString
2220 * @param classification
2223 * @param highlightFragments
2224 * @param directorySelectClass
2227 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2228 String queryString
, Classification classification
, List
<Feature
> features
,
2229 List
<Language
> languages
, boolean highlightFragments
) {
2231 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2232 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2234 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2236 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2237 languages
, descriptionElementQueryFactory
);
2239 luceneSearch
.setSortFields(sortFields
);
2240 luceneSearch
.setCdmTypRestriction(clazz
);
2241 luceneSearch
.setQuery(finalQuery
);
2242 if(highlightFragments
){
2243 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2246 return luceneSearch
;
2250 * @param queryString
2251 * @param classification
2254 * @param descriptionElementQueryFactory
2257 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2258 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2259 BooleanQuery finalQuery
= new BooleanQuery();
2260 BooleanQuery textQuery
= new BooleanQuery();
2261 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2265 if(languages
== null || languages
.size() == 0){
2266 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2268 nameQuery
= new BooleanQuery();
2269 BooleanQuery languageSubQuery
= new BooleanQuery();
2270 for(Language lang
: languages
){
2271 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2273 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2274 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2276 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2279 // text field from TextData
2280 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2282 // --- TermBase fields - by representation ----
2283 // state field from CategoricalData
2284 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2286 // state field from CategoricalData
2287 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2289 // area field from Distribution
2290 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2292 // status field from Distribution
2293 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2295 finalQuery
.add(textQuery
, Occur
.MUST
);
2296 // --- classification ----
2298 if(classification
!= null){
2299 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2302 // --- IdentifieableEntity fields - by uuid
2303 if(features
!= null && features
.size() > 0 ){
2304 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2307 // the description must be associated with a taxon
2308 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2310 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2315 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2316 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2317 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2319 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2320 * or {@link MultilanguageTextFieldBridge }
2321 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2322 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2323 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2325 * TODO move to utiliy class !!!!!!!!
2327 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2329 if(stringBuilder
== null){
2330 stringBuilder
= new StringBuilder();
2332 if(languages
== null || languages
.size() == 0){
2333 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2335 for(Language lang
: languages
){
2336 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2339 return stringBuilder
;
2343 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2344 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2345 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2347 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2350 UUID nameUuid
= taxon
.getName().getUuid();
2351 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2352 String epithetOfTaxon
= null;
2353 String infragenericEpithetOfTaxon
= null;
2354 String infraspecificEpithetOfTaxon
= null;
2355 if (taxonName
.isSpecies()){
2356 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2357 } else if (taxonName
.isInfraGeneric()){
2358 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2359 } else if (taxonName
.isInfraSpecific()){
2360 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2362 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2363 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2364 List
<String
> taxonNames
= new ArrayList
<String
>();
2366 for (TaxonNode node
: nodes
){
2367 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2368 // List<String> synonymsEpithet = new ArrayList<String>();
2370 if (node
.getClassification().equals(classification
)){
2371 if (!node
.isTopmostNode()){
2372 TaxonNode parent
= node
.getParent();
2373 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2374 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2375 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2376 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2377 Rank rankOfTaxon
= taxonName
.getRank();
2380 //create inferred synonyms for species, subspecies
2381 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2383 Synonym inferredEpithet
= null;
2384 Synonym inferredGenus
= null;
2385 Synonym potentialCombination
= null;
2387 List
<String
> propertyPaths
= new ArrayList
<String
>();
2388 propertyPaths
.add("synonym");
2389 propertyPaths
.add("synonym.name");
2390 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2391 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2393 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2394 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2396 List
<TaxonRelationship
> taxonRelListParent
= null;
2397 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2398 if (doWithMisappliedNames
){
2399 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2400 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2404 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2407 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2408 Synonym syn
= synonymRelationOfParent
.getSynonym();
2410 inferredEpithet
= createInferredEpithets(taxon
,
2411 zooHashMap
, taxonName
, epithetOfTaxon
,
2412 infragenericEpithetOfTaxon
,
2413 infraspecificEpithetOfTaxon
,
2414 taxonNames
, parentName
,
2418 inferredSynonyms
.add(inferredEpithet
);
2419 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2420 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2423 if (doWithMisappliedNames
){
2425 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2426 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2428 inferredEpithet
= createInferredEpithets(taxon
,
2429 zooHashMap
, taxonName
, epithetOfTaxon
,
2430 infragenericEpithetOfTaxon
,
2431 infraspecificEpithetOfTaxon
,
2432 taxonNames
, parentName
,
2435 inferredSynonyms
.add(inferredEpithet
);
2436 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2437 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2441 if (!taxonNames
.isEmpty()){
2442 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2443 ZoologicalName name
;
2444 if (!synNotInCDM
.isEmpty()){
2445 inferredSynonymsToBeRemoved
.clear();
2447 for (Synonym syn
:inferredSynonyms
){
2448 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2449 if (!synNotInCDM
.contains(name
.getNameCache())){
2450 inferredSynonymsToBeRemoved
.add(syn
);
2454 // Remove identified Synonyms from inferredSynonyms
2455 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2456 inferredSynonyms
.remove(synonym
);
2461 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2464 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2465 TaxonNameBase synName
;
2466 ZoologicalName inferredSynName
;
2468 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2469 inferredGenus
= createInferredGenus(taxon
,
2470 zooHashMap
, taxonName
, epithetOfTaxon
,
2471 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2473 inferredSynonyms
.add(inferredGenus
);
2474 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2475 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2480 if (doWithMisappliedNames
){
2482 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2483 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2484 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2486 inferredSynonyms
.add(inferredGenus
);
2487 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2488 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2493 if (!taxonNames
.isEmpty()){
2494 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2495 ZoologicalName name
;
2496 if (!synNotInCDM
.isEmpty()){
2497 inferredSynonymsToBeRemoved
.clear();
2499 for (Synonym syn
:inferredSynonyms
){
2500 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2501 if (!synNotInCDM
.contains(name
.getNameCache())){
2502 inferredSynonymsToBeRemoved
.add(syn
);
2506 // Remove identified Synonyms from inferredSynonyms
2507 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2508 inferredSynonyms
.remove(synonym
);
2513 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2515 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2516 ZoologicalName inferredSynName
;
2517 //for all synonyms of the parent...
2518 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2519 TaxonNameBase synName
;
2520 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2521 synName
= synParent
.getName();
2523 HibernateProxyHelper
.deproxy(synParent
);
2525 // Set the sourceReference
2526 sourceReference
= synParent
.getSec();
2528 // Determine the idInSource
2529 String idInSourceParent
= getIdInSource(synParent
);
2531 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2532 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2533 String synParentInfragenericName
= null;
2534 String synParentSpecificEpithet
= null;
2536 if (parentSynZooName
.isInfraGeneric()){
2537 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2539 if (parentSynZooName
.isSpecies()){
2540 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2543 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2544 synonymsGenus.put(synGenusName, idInSource);
2547 //for all synonyms of the taxon
2549 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2551 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2552 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2553 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2555 synParentInfragenericName
,
2556 synParentSpecificEpithet
, syn
, zooHashMap
);
2558 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2559 inferredSynonyms
.add(potentialCombination
);
2560 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2561 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2568 if (doWithMisappliedNames
){
2570 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2572 TaxonNameBase misappliedParentName
;
2574 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2575 misappliedParentName
= misappliedParent
.getName();
2577 HibernateProxyHelper
.deproxy(misappliedParent
);
2579 // Set the sourceReference
2580 sourceReference
= misappliedParent
.getSec();
2582 // Determine the idInSource
2583 String idInSourceParent
= getIdInSource(misappliedParent
);
2585 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2586 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2587 String synParentInfragenericName
= null;
2588 String synParentSpecificEpithet
= null;
2590 if (parentSynZooName
.isInfraGeneric()){
2591 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2593 if (parentSynZooName
.isSpecies()){
2594 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2598 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2599 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2600 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2601 potentialCombination
= createPotentialCombination(
2602 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2604 synParentInfragenericName
,
2605 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2608 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2609 inferredSynonyms
.add(potentialCombination
);
2610 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2611 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2616 if (!taxonNames
.isEmpty()){
2617 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2618 ZoologicalName name
;
2619 if (!synNotInCDM
.isEmpty()){
2620 inferredSynonymsToBeRemoved
.clear();
2621 for (Synonym syn
:inferredSynonyms
){
2623 name
= (ZoologicalName
) syn
.getName();
2624 }catch (ClassCastException e
){
2625 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2627 if (!synNotInCDM
.contains(name
.getNameCache())){
2628 inferredSynonymsToBeRemoved
.add(syn
);
2631 // Remove identified Synonyms from inferredSynonyms
2632 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2633 inferredSynonyms
.remove(synonym
);
2639 logger
.info("The synonymrelationship type is not defined.");
2640 return inferredSynonyms
;
2647 return inferredSynonyms
;
2650 private Synonym
createPotentialCombination(String idInSourceParent
,
2651 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2652 String synParentInfragenericName
, String synParentSpecificEpithet
,
2653 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2654 Synonym potentialCombination
;
2655 Reference sourceReference
;
2656 ZoologicalName inferredSynName
;
2657 HibernateProxyHelper
.deproxy(syn
);
2659 // Set sourceReference
2660 sourceReference
= syn
.getSec();
2661 if (sourceReference
== null){
2662 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2664 if (!parentSynZooName
.getTaxa().isEmpty()){
2665 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2667 sourceReference
= taxon
.getSec();
2670 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2672 String synTaxonInfraSpecificName
= null;
2674 if (parentSynZooName
.isSpecies()){
2675 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2678 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2679 synonymsEpithet.add(epithetName);
2682 //create potential combinations...
2683 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2685 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2686 if (zooSynName
.isSpecies()){
2687 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2688 if (parentSynZooName
.isInfraGeneric()){
2689 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2692 if (zooSynName
.isInfraSpecific()){
2693 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2694 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2696 if (parentSynZooName
.isInfraGeneric()){
2697 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2701 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2703 // Set the sourceReference
2704 potentialCombination
.setSec(sourceReference
);
2707 // Determine the idInSource
2708 String idInSourceSyn
= getIdInSource(syn
);
2710 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2711 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2712 inferredSynName
.addSource(originalSource
);
2713 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2714 potentialCombination
.addSource(originalSource
);
2717 inferredSynName
.generateTitle();
2719 return potentialCombination
;
2722 private Synonym
createInferredGenus(Taxon taxon
,
2723 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2724 String epithetOfTaxon
, String genusOfTaxon
,
2725 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2728 Synonym inferredGenus
;
2729 TaxonNameBase synName
;
2730 ZoologicalName inferredSynName
;
2731 synName
=syn
.getName();
2732 HibernateProxyHelper
.deproxy(syn
);
2734 // Determine the idInSource
2735 String idInSourceSyn
= getIdInSource(syn
);
2736 String idInSourceTaxon
= getIdInSource(taxon
);
2737 // Determine the sourceReference
2738 Reference sourceReference
= syn
.getSec();
2740 //logger.warn(sourceReference.getTitleCache());
2742 synName
= syn
.getName();
2743 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2744 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2745 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2746 synonymsEpithet.add(synSpeciesEpithetName);
2749 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2750 //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
2753 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2754 if (zooParentName
.isInfraGeneric()){
2755 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2758 if (taxonName
.isSpecies()){
2759 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2761 if (taxonName
.isInfraSpecific()){
2762 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2763 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2767 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2769 // Set the sourceReference
2770 inferredGenus
.setSec(sourceReference
);
2772 // Add the original source
2773 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2774 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2775 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2776 inferredGenus
.addSource(originalSource
);
2778 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2779 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2780 inferredSynName
.addSource(originalSource
);
2781 originalSource
= null;
2784 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2785 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2786 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2787 inferredGenus
.addSource(originalSource
);
2789 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2790 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2791 inferredSynName
.addSource(originalSource
);
2792 originalSource
= null;
2795 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2797 inferredSynName
.generateTitle();
2800 return inferredGenus
;
2803 private Synonym
createInferredEpithets(Taxon taxon
,
2804 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2805 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2806 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2807 TaxonNameBase parentName
, TaxonBase syn
) {
2809 Synonym inferredEpithet
;
2810 TaxonNameBase
<?
,?
> synName
;
2811 ZoologicalName inferredSynName
;
2812 HibernateProxyHelper
.deproxy(syn
);
2814 // Determine the idInSource
2815 String idInSourceSyn
= getIdInSource(syn
);
2816 String idInSourceTaxon
= getIdInSource(taxon
);
2817 // Determine the sourceReference
2818 Reference
<?
> sourceReference
= syn
.getSec();
2820 if (sourceReference
== null){
2821 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2822 sourceReference
= taxon
.getSec();
2825 synName
= syn
.getName();
2826 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2827 String synGenusName
= zooSynName
.getGenusOrUninomial();
2828 String synInfraGenericEpithet
= null;
2829 String synSpecificEpithet
= null;
2831 if (zooSynName
.getInfraGenericEpithet() != null){
2832 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2835 if (zooSynName
.isInfraSpecific()){
2836 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2839 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2840 synonymsGenus.put(synGenusName, idInSource);
2843 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2845 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2846 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2847 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2849 inferredSynName
.setGenusOrUninomial(synGenusName
);
2851 if (parentName
.isInfraGeneric()){
2852 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2854 if (taxonName
.isSpecies()){
2855 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2856 }else if (taxonName
.isInfraSpecific()){
2857 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2858 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2861 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2863 // Set the sourceReference
2864 inferredEpithet
.setSec(sourceReference
);
2866 /* Add the original source
2867 if (idInSource != null) {
2868 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2871 Reference citation = getCitation(syn);
2872 if (citation != null) {
2873 originalSource.setCitation(citation);
2874 inferredEpithet.addSource(originalSource);
2877 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2880 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2881 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2883 inferredEpithet
.addSource(originalSource
);
2885 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2886 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2888 inferredSynName
.addSource(originalSource
);
2892 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2894 inferredSynName
.generateTitle();
2895 return inferredEpithet
;
2899 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2900 * Very likely only useful for createInferredSynonyms().
2905 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2906 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2907 if (taxonName
== null) {
2908 taxonName
= zooHashMap
.get(uuid
);
2914 * Returns the idInSource for a given Synonym.
2917 private String
getIdInSource(TaxonBase taxonBase
) {
2918 String idInSource
= null;
2919 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2920 if (sources
.size() == 1) {
2921 IdentifiableSource source
= sources
.iterator().next();
2922 if (source
!= null) {
2923 idInSource
= source
.getIdInSource();
2925 } else if (sources
.size() > 1) {
2928 for (IdentifiableSource source
: sources
) {
2929 idInSource
+= source
.getIdInSource();
2930 if (count
< sources
.size()) {
2935 } else if (sources
.size() == 0){
2936 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2945 * Returns the citation for a given Synonym.
2948 private Reference
getCitation(Synonym syn
) {
2949 Reference citation
= null;
2950 Set
<IdentifiableSource
> sources
= syn
.getSources();
2951 if (sources
.size() == 1) {
2952 IdentifiableSource source
= sources
.iterator().next();
2953 if (source
!= null) {
2954 citation
= source
.getCitation();
2956 } else if (sources
.size() > 1) {
2957 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2964 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2965 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2967 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2968 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2969 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2971 return inferredSynonyms
;
2975 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2978 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2980 // TODO quickly implemented, create according dao !!!!
2981 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2982 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2983 List
<Classification
> list
= new ArrayList
<Classification
>();
2985 if (taxonBase
== null) {
2989 taxonBase
= load(taxonBase
.getUuid());
2991 if (taxonBase
instanceof Taxon
) {
2992 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2994 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2995 nodes
.addAll(taxon
.getTaxonNodes());
2998 for (TaxonNode node
: nodes
) {
2999 classifications
.add(node
.getClassification());
3001 list
.addAll(classifications
);
3006 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3007 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3008 // Create new synonym using concept name
3009 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3010 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3012 // Remove concept relation from taxon
3013 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3018 // Create a new synonym for the taxon
3019 SynonymRelationship synonymRelationship
;
3020 if (synonymRelationshipType
!= null
3021 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3022 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3024 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3027 this.saveOrUpdate(toTaxon
);
3028 //TODO: configurator and classification
3029 this.deleteTaxon(fromTaxon
, null, null);
3030 return synonymRelationship
.getSynonym();
3034 public List
<String
> isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3035 List
<String
> result
= new ArrayList
<String
>();
3036 Set
<CdmBase
> references
= commonService
.getReferencingObjects(taxonBase
);
3037 if (taxonBase
instanceof Taxon
){
3038 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3039 result
= isDeletableForTaxon(references
, taxonConfig
);
3041 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3042 result
= isDeletableForSynonym(references
, synonymConfig
);
3047 private List
<String
> isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3049 List
<String
> result
= new ArrayList
<String
>();
3050 for (CdmBase ref
: references
){
3051 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3052 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3053 result
.add(message
);
3059 private List
<String
> isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3061 List
<String
> result
= new ArrayList
<String
>();
3062 for (CdmBase ref
: references
){
3063 if (!(ref
instanceof TaxonNameBase
)){
3064 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3065 message
= "The Taxon can't be deleted as long as it has synonyms.";
3066 result
.add(message
);
3068 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3069 message
= "The Taxon can't be deleted as long as it has factual data.";
3070 result
.add(message
);
3073 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3074 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3075 result
.add(message
);
3077 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3078 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3079 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3080 result
.add(message
);
3082 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3083 result
.add(message
);
3086 if (ref
instanceof PolytomousKeyNode
){
3087 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3088 result
.add(message
);
3091 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3092 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3093 result
.add(message
);
3098 /* //PolytomousKeyNode
3099 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3100 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3105 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3106 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3107 result
.add(message
);
3111 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3112 message
= "Taxon can't be deleted as it is used in a determination event";
3113 result
.add(message
);