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
.IncludedTaxonConfiguration
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.SynonymDeletionConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
47 import eu
.etaxonomy
.cdm
.api
.service
.dto
.FindByIdentifierDTO
;
48 import eu
.etaxonomy
.cdm
.api
.service
.dto
.IncludedTaxaDTO
;
49 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
50 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
51 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
52 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
53 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
61 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
62 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
63 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
64 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
65 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
66 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
67 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
68 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
69 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
70 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
71 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
72 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
73 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
74 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
75 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
76 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
77 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
78 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
79 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
80 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
81 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
82 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
83 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
84 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
85 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
86 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
87 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
88 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
89 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
90 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
91 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
92 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
93 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
94 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
95 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
96 import eu
.etaxonomy
.cdm
.model
.molecular
.AmplificationResult
;
97 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
98 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
99 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
100 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
101 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
102 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
103 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
104 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
105 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
106 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
107 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
108 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
115 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
116 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
117 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
118 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
119 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
122 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
123 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
124 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
125 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
126 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
127 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
128 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
129 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
130 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
134 * @author a.kohlbecker
139 @Transactional(readOnly
= true)
140 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
141 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
143 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
145 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
147 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
151 private ITaxonNameDao nameDao
;
154 private INameService nameService
;
157 private ITaxonNodeService nodeService
;
160 private ICdmGenericDao genericDao
;
163 private IDescriptionService descriptionService
;
166 private IOrderedTermVocabularyDao orderedVocabularyDao
;
169 private IOccurrenceDao occurrenceDao
;
172 private IClassificationDao classificationDao
;
175 private AbstractBeanInitializer beanInitializer
;
178 private ILuceneIndexToolProvider luceneIndexToolProvider
;
183 public TaxonServiceImpl(){
184 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
188 * FIXME Candidate for harmonization
189 * rename searchByName ?
192 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
193 return dao
.getTaxaByName(name
, sec
);
197 * FIXME Candidate for harmonization
198 * merge with getRootTaxa(Reference sec, ..., ...)
200 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
203 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
204 if (cdmFetch
== null){
205 cdmFetch
= CdmFetch
.NO_FETCH();
207 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
211 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
212 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
216 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
217 return dao
.getAllRelationships(limit
, start
);
221 * FIXME Candidate for harmonization
222 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
226 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
228 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
229 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
230 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
231 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
232 return taxonRelTypeVocabulary
;
239 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
242 @Transactional(readOnly
= false)
243 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
245 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
246 synonymName
.removeTaxonBase(synonym
);
247 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
248 taxonName
.removeTaxonBase(acceptedTaxon
);
250 synonym
.setName(taxonName
);
251 acceptedTaxon
.setName(synonymName
);
253 // the accepted taxon needs a new uuid because the concept has changed
254 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
255 //acceptedTaxon.setUuid(UUID.randomUUID());
260 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
264 @Transactional(readOnly
= false)
265 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
267 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
268 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
269 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
271 //check synonym is not homotypic
272 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
273 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
274 throw new HomotypicalGroupChangeException(message
);
277 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
279 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
280 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
281 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
283 for (Synonym heteroSynonym
: heteroSynonyms
){
284 if (synonym
.equals(heteroSynonym
)){
285 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
288 //move synonyms in same homotypic group to new accepted taxon
289 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
293 //synonym.getName().removeTaxonBase(synonym);
296 // deleteSynonym(synonym, taxon, false);
299 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
300 config
.setDeleteNameIfPossible(false);
301 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
303 } catch (Exception e
) {
304 logger
.info("Can't delete old synonym from database");
308 return newAcceptedTaxon
;
313 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
315 // Get name from synonym
316 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
318 /* // remove synonym from taxon
319 toTaxon.removeSynonym(synonym);
321 // Create a taxon with synonym name
322 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
324 // Add taxon relation
325 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
327 // since we are swapping names, we have to detach the name from the synonym completely.
328 // Otherwise the synonym will still be in the list of typified names.
329 // synonym.getName().removeTaxonBase(synonym);
330 this.deleteSynonym(synonym
, null);
337 * @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)
339 @Transactional(readOnly
= false)
341 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
342 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
344 TaxonNameBase synonymName
= synonym
.getName();
345 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
349 oldHomotypicalGroup
.removeTypifiedName(synonymName
, false);
350 newHomotypicalGroup
.addTypifiedName(synonymName
);
352 //remove existing basionym relationships
353 synonymName
.removeBasionyms();
355 //add basionym relationship
356 if (setBasionymRelationIfApplicable
){
357 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
358 for (TaxonNameBase basionym
: basionyms
){
359 synonymName
.addBasionym(basionym
);
363 //set synonym relationship correctly
364 // SynonymRelationship relToTaxon = null;
365 boolean relToTargetTaxonExists
= false;
366 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
367 for (SynonymRelationship rel
: existingRelations
){
368 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
369 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
370 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
371 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
372 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
373 rel
.setType(newRelationType
);
374 //TODO handle citation and microCitation
377 relToTargetTaxonExists
= true;
379 if (removeFromOtherTaxa
){
380 acceptedTaxon
.removeSynonym(synonym
, false);
386 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
387 Taxon acceptedTaxon
= targetTaxon
;
388 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
389 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
390 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
391 //TODO handle citation and microCitation
392 Reference citation
= null;
393 String microCitation
= null;
394 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
401 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
404 @Transactional(readOnly
= false)
405 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
407 clazz
= TaxonBase
.class;
409 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
414 protected void setDao(ITaxonDao dao
) {
419 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
420 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
422 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
423 if(numberOfResults
> 0) { // no point checking again
424 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
427 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
431 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
432 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
434 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
435 if(numberOfResults
> 0) { // no point checking again
436 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
443 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
444 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
446 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
447 if(numberOfResults
> 0) { // no point checking again
448 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
454 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
455 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
457 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
458 if(numberOfResults
> 0) { // no point checking again
459 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
461 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
465 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
466 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
468 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
469 if(numberOfResults
> 0) { // no point checking again
470 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
476 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(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
);
483 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
487 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
488 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
489 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
493 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
494 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
496 List
<Taxon
> list
= new ArrayList
<Taxon
>();
499 Synonym synonym
= null;
502 synonym
= (Synonym
) dao
.load(synonymUuid
);
503 } catch (ClassCastException e
){
504 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
505 } catch (NullPointerException e
){
506 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
509 Classification classificationFilter
= null;
510 if(classificationUuid
!= null){
512 classificationFilter
= classificationDao
.load(classificationUuid
);
513 } catch (NullPointerException e
){
514 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
516 if(classificationFilter
== null){
521 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
522 if(count
> (pageSize
* pageNumber
)){
523 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
526 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
531 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
532 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
534 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
535 relatedTaxa
.remove(taxon
);
536 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
542 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
543 * <code>taxon</code> supplied as parameter.
546 * @param includeRelationships
548 * @param maxDepth can be <code>null</code> for infinite depth
551 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
557 if(includeRelationships
.isEmpty()){
561 if(maxDepth
!= null) {
564 if(logger
.isDebugEnabled()){
565 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
567 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
568 for (TaxonRelationship taxRel
: taxonRelationships
) {
571 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
574 // filter by includeRelationships
575 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
576 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
577 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
578 if(logger
.isDebugEnabled()){
579 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
581 taxa
.add(taxRel
.getToTaxon());
582 if(maxDepth
== null || maxDepth
> 0) {
583 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
586 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
587 taxa
.add(taxRel
.getFromTaxon());
588 if(logger
.isDebugEnabled()){
589 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
591 if(maxDepth
== null || maxDepth
> 0) {
592 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
602 * @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)
605 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
606 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
608 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
609 if(numberOfResults
> 0) { // no point checking again
610 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
613 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
617 * @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)
620 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
621 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
623 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
624 if(numberOfResults
> 0) { // no point checking again
625 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
628 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
632 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
635 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
636 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
637 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
640 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
643 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
644 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
645 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
653 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
656 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
657 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
658 return t
.getHomotypicSynonymsByHomotypicGroup();
662 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
665 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
666 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
667 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
668 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
669 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
670 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
672 return heterotypicSynonymyGroups
;
676 public List
<UuidAndTitleCache
<IdentifiableEntity
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
678 List
<UuidAndTitleCache
<IdentifiableEntity
>> results
= new ArrayList
<UuidAndTitleCache
<IdentifiableEntity
>>();
681 if (configurator
.isDoSynonyms() || configurator
.isDoTaxa() || configurator
.isDoNamesWithoutTaxa()){
682 results
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.isDoNamesWithoutTaxa(), configurator
.isDoMisappliedNames(),configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
684 if (configurator
.isDoTaxaByCommonNames()) {
685 //if(configurator.getPageSize() == null ){
686 List
<UuidAndTitleCache
<IdentifiableEntity
>> commonNameResults
= dao
.getTaxaByCommonNameForEditor(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
687 if(commonNameResults
!= null){
688 results
.addAll(commonNameResults
);
696 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
699 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
701 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
702 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
703 List
<TaxonBase
> taxa
= null;
706 long numberTaxaResults
= 0L;
709 List
<String
> propertyPath
= new ArrayList
<String
>();
710 if(configurator
.getTaxonPropertyPath() != null){
711 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
715 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
716 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
718 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
719 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
720 configurator
.getNamedAreas());
723 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
724 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
725 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
726 configurator
.getMatchMode(), configurator
.getNamedAreas(),
727 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
731 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
734 results
.addAll(taxa
);
737 numberOfResults
+= numberTaxaResults
;
739 // Names without taxa
740 if (configurator
.isDoNamesWithoutTaxa()) {
741 int numberNameResults
= 0;
743 List
<?
extends TaxonNameBase
<?
,?
>> names
=
744 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
745 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
746 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
747 if (names
.size() > 0) {
748 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
749 if (taxonName
.getTaxonBases().size() == 0) {
750 results
.add(taxonName
);
754 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
755 numberOfResults
+= numberNameResults
;
759 // Taxa from common names
761 if (configurator
.isDoTaxaByCommonNames()) {
762 taxa
= new ArrayList
<TaxonBase
>();
763 numberTaxaResults
= 0;
764 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
765 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
767 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
768 List
<Taxon
> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
769 taxa
.addAll(commonNameResults
);
772 results
.addAll(taxa
);
774 numberOfResults
+= numberTaxaResults
;
778 return new DefaultPagerImpl
<IdentifiableEntity
>
779 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
782 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
783 return dao
.getUuidAndTitleCache();
787 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
790 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
791 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
792 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
793 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
794 for (TaxonDescription taxDesc
: descriptions
){
795 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
796 for (DescriptionElementBase descElem
: elements
){
797 for(Media media
: descElem
.getMedia()){
799 //find the best matching representation
800 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
809 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
812 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
813 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
818 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
821 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
822 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
823 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
825 // logger.setLevel(Level.TRACE);
826 // Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
828 logger
.trace("listMedia() - START");
830 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
831 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
832 List
<Media
> nonImageGalleryImages
= new ArrayList
<Media
>();
834 if (limitToGalleries
== null) {
835 limitToGalleries
= false;
838 // --- resolve related taxa
839 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
840 logger
.trace("listMedia() - resolve related taxa");
841 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
844 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
846 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
847 logger
.trace("listMedia() - includeTaxonDescriptions");
848 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
849 // --- TaxonDescriptions
850 for (Taxon t
: taxa
) {
851 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
853 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
854 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
855 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
856 for (Media media
: element
.getMedia()) {
857 if(taxonDescription
.isImageGallery()){
858 taxonMedia
.add(media
);
861 nonImageGalleryImages
.add(media
);
867 //put images from image gallery first (#3242)
868 taxonMedia
.addAll(nonImageGalleryImages
);
872 if(includeOccurrences
!= null && includeOccurrences
) {
873 logger
.trace("listMedia() - includeOccurrences");
874 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
876 for (Taxon t
: taxa
) {
877 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
879 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
881 // direct media removed from specimen #3597
882 // taxonMedia.addAll(occurrence.getMedia());
884 // SpecimenDescriptions
885 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
886 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
887 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
888 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
889 for (DescriptionElementBase element
: elements
) {
890 for (Media media
: element
.getMedia()) {
891 taxonMedia
.add(media
);
898 //TODO why may collections have media attached? #
899 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
900 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
901 if (derivedUnit
.getCollection() != null){
902 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
906 // pherograms & gelPhotos
907 if (occurrence
.isInstanceOf(DnaSample
.class)) {
908 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
909 Set
<Sequence
> sequences
= dnaSample
.getSequences();
910 //we do show only those gelPhotos which lead to a consensus sequence
911 for (Sequence sequence
: sequences
) {
912 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
913 for (SingleRead singleRead
: sequence
.getSingleReads()){
914 AmplificationResult amplification
= singleRead
.getAmplificationResult();
915 dnaRelatedMedia
.add(amplification
.getGelPhoto());
916 dnaRelatedMedia
.add(singleRead
.getPherogram());
917 dnaRelatedMedia
.remove(null);
919 taxonMedia
.addAll(dnaRelatedMedia
);
926 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
927 logger
.trace("listMedia() - includeTaxonNameDescriptions");
928 // --- TaxonNameDescription
929 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
930 for (Taxon t
: taxa
) {
931 nameDescriptions
.addAll(t
.getName().getDescriptions());
933 for(TaxonNameDescription nameDescription
: nameDescriptions
){
934 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
935 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
936 for (DescriptionElementBase element
: elements
) {
937 for (Media media
: element
.getMedia()) {
938 taxonMedia
.add(media
);
946 logger
.trace("listMedia() - initialize");
947 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
949 logger
.trace("listMedia() - END");
955 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
958 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
959 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
963 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
966 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
967 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
971 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
974 public int countAllRelationships() {
975 return this.dao
.countAllRelationships();
982 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
985 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
986 return this.dao
.findIdenticalTaxonNames(propertyPath
);
991 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
994 public DeleteResult
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
997 config
= new TaxonDeletionConfigurator();
1000 DeleteResult result
= isDeletable(taxon
, config
);
1003 // --- DeleteSynonymRelations
1004 if (config
.isDeleteSynonymRelations()){
1005 boolean removeSynonymNameFromHomotypicalGroup
= false;
1006 // use tmp Set to avoid concurrent modification
1007 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1008 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1009 for (SynonymRelationship synRel
: synRelsToDelete
){
1010 Synonym synonym
= synRel
.getSynonym();
1011 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1012 // this will cause hibernate to delete the relationship since
1013 // the SynonymRelationship field on both is annotated with removeOrphan
1014 // so no further explicit deleting of the relationship should be done here
1015 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1017 // --- DeleteSynonymsIfPossible
1018 if (config
.isDeleteSynonymsIfPossible()){
1020 boolean newHomotypicGroupIfNeeded
= true;
1021 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1022 deleteSynonym(synonym
, taxon
, synConfig
);
1024 // relationship will be deleted by hibernate automatically,
1025 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1027 // deleteSynonymRelationships(synonym, taxon);
1032 // --- DeleteTaxonRelationships
1033 if (! config
.isDeleteTaxonRelationships()){
1034 if (taxon
.getTaxonRelations().size() > 0){
1035 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1036 "Remove taxon from all relations to other taxa prior to deletion.";
1037 // throw new ReferencedObjectUndeletableException(message);
1040 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1041 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1042 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1043 if (taxon
.equals(taxRel
.getToTaxon())){
1044 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1048 taxon
.removeTaxonRelation(taxRel
);
1049 /*if (taxFrom.equals(taxon)){
1051 this.deleteTaxon(taxTo, taxConf, classification);
1052 } catch(DataChangeNoRollbackException e){
1053 logger.debug("A related taxon will not be deleted." + e.getMessage());
1057 this.deleteTaxon(taxFrom, taxConf, classification);
1058 } catch(DataChangeNoRollbackException e){
1059 logger.debug("A related taxon will not be deleted." + e.getMessage());
1067 if (config
.isDeleteDescriptions()){
1068 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1069 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1070 for (TaxonDescription desc
: descriptions
){
1071 //TODO use description delete configurator ?
1072 //FIXME check if description is ALWAYS deletable
1073 if (desc
.getDescribedSpecimenOrObservation() != null){
1074 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1075 " which also describes specimens or abservations";
1076 //throw new ReferencedObjectUndeletableException(message);
1078 removeDescriptions
.add(desc
);
1079 descriptionService
.delete(desc
);
1082 for (TaxonDescription desc
: removeDescriptions
){
1083 taxon
.removeDescription(desc
);
1088 /* //check references with only reverse mapping
1089 String message = checkForReferences(taxon);
1090 if (message != null){
1091 //throw new ReferencedObjectUndeletableException(message.toString());
1094 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1095 //if (taxon.getTaxonNodes().size() > 0){
1096 // 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.";
1097 // throw new ReferencedObjectUndeletableException(message);
1100 if (taxon
.getTaxonNodes().size() != 0){
1101 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1102 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1103 TaxonNode node
= null;
1104 boolean deleteChildren
;
1105 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1106 deleteChildren
= true;
1108 deleteChildren
= false;
1110 boolean success
= true;
1111 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1112 while (iterator
.hasNext()){
1113 node
= iterator
.next();
1114 if (node
.getClassification().equals(classification
)){
1120 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1121 nodeService
.delete(node
);
1124 result
.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
1126 } else if (config
.isDeleteInAllClassifications()){
1127 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1128 nodesList
.addAll(taxon
.getTaxonNodes());
1130 for (ITaxonTreeNode treeNode
: nodesList
){
1131 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1132 if(!deleteChildren
){
1133 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1134 //nodesList.addAll(taxonNode.getChildNodes());
1135 for (Object childNode: childNodes){
1136 TaxonNode childNodeCast = (TaxonNode) childNode;
1137 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1141 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1142 deleteTaxon(childNode.getTaxon(), config, classification);
1145 // taxon.removeTaxonNode(taxonNode);
1146 //nodeService.delete(taxonNode);
1149 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1150 for (Object childNode
: childNodes
){
1151 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1152 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1155 //taxon.removeTaxonNode(taxonNode);
1158 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1159 DeleteResult resultNodes
= nodeService
.deleteTaxonNodes(nodesList
, config
);
1160 if (!resultNodes
.isOk()){
1161 result
.addExceptions(resultNodes
.getExceptions());
1162 result
.setStatus(resultNodes
.getStatus());
1167 result
.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
1173 //PolytomousKey TODO
1177 if (config
.isDeleteNameIfPossible()){
1180 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1181 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1182 //check whether taxon will be deleted or not
1183 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1184 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1185 //name.removeTaxonBase(taxon);
1186 //nameService.saveOrUpdate(name);
1187 taxon
.setName(null);
1188 //dao.delete(taxon);
1189 DeleteResult nameResult
= new DeleteResult();
1191 //remove name if possible (and required)
1192 if (name
!= null && config
.isDeleteNameIfPossible()){
1193 nameResult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1196 if (nameResult
.isError()){
1197 //result.setError();
1198 result
.addRelatedObject(name
);
1199 result
.addExceptions(nameResult
.getExceptions());
1207 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1209 for (TaxonDescription desc: descriptions){
1210 if (config.isDeleteDescriptions()){
1211 //TODO use description delete configurator ?
1212 //FIXME check if description is ALWAYS deletable
1213 taxon.removeDescription(desc);
1214 descriptionService.delete(desc);
1216 if (desc.getDescribedSpecimenOrObservations().size()>0){
1217 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1218 " which also describes specimens or observations";
1219 throw new ReferencedObjectUndeletableException(message);
1224 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1226 UUID uuid
= dao
.delete(taxon
);
1228 }catch(Exception e
){
1229 result
.addException(e
);
1235 result
.addException(new Exception("The Taxon can't be deleted."));
1240 // List<Exception> exceptions = new ArrayList<Exception>();
1241 // for (String message: referencedObjects){
1242 // ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message);
1243 // exceptions.add(exception);
1245 // result.addExceptions(exceptions);
1246 // result.setError();
1253 private String
checkForReferences(Taxon taxon
){
1254 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1255 for (CdmBase referencingObject
: referencingObjects
){
1256 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1257 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1258 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";
1264 /* //PolytomousKeyNode
1265 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1266 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1271 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1272 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1277 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1278 String message
= "Taxon can't be deleted as it is used in a determination event";
1284 referencingObjects
= null;
1288 private boolean checkForPolytomousKeys(Taxon taxon
){
1289 boolean result
= false;
1290 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1291 if (!list
.isEmpty()) {
1297 @Transactional(readOnly
= false)
1298 public UUID
delete(Synonym syn
){
1299 UUID result
= syn
.getUuid();
1300 this.deleteSynonym(syn
, null);
1307 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1309 @Transactional(readOnly
= false)
1311 public DeleteResult
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1312 return deleteSynonym(synonym
, null, config
);
1318 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1320 @Transactional(readOnly
= false)
1322 public DeleteResult
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1323 DeleteResult result
= new DeleteResult();
1324 if (synonym
== null){
1329 if (config
== null){
1330 config
= new SynonymDeletionConfigurator();
1332 result
= isDeletable(synonym
, config
);
1336 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1338 //remove synonymRelationship
1339 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1341 taxonSet
.add(taxon
);
1343 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1345 for (Taxon relatedTaxon
: taxonSet
){
1346 relatedTaxon
= HibernateProxyHelper
.deproxy(relatedTaxon
, Taxon
.class);
1347 relatedTaxon
.removeSynonym(synonym
, false);
1348 this.saveOrUpdate(relatedTaxon
);
1350 this.saveOrUpdate(synonym
);
1352 //TODO remove name from homotypical group?
1354 //remove synonym (if necessary)
1356 if (synonym
.getSynonymRelations().isEmpty()){
1357 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1358 synonym
.setName(null);
1359 dao
.delete(synonym
);
1361 //remove name if possible (and required)
1362 if (name
!= null && config
.isDeleteNameIfPossible()){
1364 DeleteResult nameDeleteresult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1365 if (nameDeleteresult
.isAbort()){
1366 result
.addExceptions(nameDeleteresult
.getExceptions());
1367 result
.addUpdatedObject(name
);
1374 result
.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
1382 // List<Exception> exceptions = new ArrayList<Exception>();
1383 // for (String message :messages){
1384 // exceptions.add(new ReferencedObjectUndeletableException(message));
1386 // result.setError();
1387 // result.addExceptions(exceptions);
1396 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1399 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1401 return this.dao
.findIdenticalNamesNew(propertyPath
);
1405 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1408 public String
getPhylumName(TaxonNameBase name
){
1409 return this.dao
.getPhylumName(name
);
1413 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1416 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1417 return dao
.deleteSynonymRelationships(syn
, taxon
);
1421 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1424 public long deleteSynonymRelationships(Synonym syn
) {
1425 return dao
.deleteSynonymRelationships(syn
, null);
1430 * @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)
1433 public List
<SynonymRelationship
> listSynonymRelationships(
1434 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1435 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1436 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1438 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1439 if(numberOfResults
> 0) { // no point checking again
1440 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1446 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1449 public Taxon
findBestMatchingTaxon(String taxonName
) {
1450 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1451 config
.setTaxonNameTitle(taxonName
);
1452 return findBestMatchingTaxon(config
);
1458 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1460 Taxon bestCandidate
= null;
1462 // 1. search for acceptet taxa
1463 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1464 boolean bestCandidateMatchesSecUuid
= false;
1465 boolean bestCandidateIsInClassification
= false;
1466 int countEqualCandidates
= 0;
1467 for(TaxonBase taxonBaseCandidate
: taxonList
){
1468 if(taxonBaseCandidate
instanceof Taxon
){
1469 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1470 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1471 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1473 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1474 bestCandidate
= newCanditate
;
1475 countEqualCandidates
= 1;
1476 bestCandidateMatchesSecUuid
= true;
1480 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1481 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1483 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1484 bestCandidate
= newCanditate
;
1485 countEqualCandidates
= 1;
1486 bestCandidateIsInClassification
= true;
1489 if (bestCandidate
== null){
1490 bestCandidate
= newCanditate
;
1491 countEqualCandidates
= 1;
1495 }else{ //not Taxon.class
1498 countEqualCandidates
++;
1501 if (bestCandidate
!= null){
1502 if(countEqualCandidates
> 1){
1503 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1504 return bestCandidate
;
1506 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1507 return bestCandidate
;
1512 // 2. search for synonyms
1513 if (config
.isIncludeSynonyms()){
1514 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1515 for(TaxonBase taxonBase
: synonymList
){
1516 if(taxonBase
instanceof Synonym
){
1517 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1518 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1519 if(!acceptetdCandidates
.isEmpty()){
1520 bestCandidate
= acceptetdCandidates
.iterator().next();
1521 if(acceptetdCandidates
.size() == 1){
1522 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1523 return bestCandidate
;
1525 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1526 return bestCandidate
;
1528 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1534 } catch (Exception e
){
1536 e
.printStackTrace();
1539 return bestCandidate
;
1542 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1543 UUID configClassificationUuid
= config
.getClassificationUuid();
1544 if (configClassificationUuid
== null){
1547 for (TaxonNode node
: taxon
.getTaxonNodes()){
1548 UUID classUuid
= node
.getClassification().getUuid();
1549 if (configClassificationUuid
.equals(classUuid
)){
1556 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1557 UUID configSecUuid
= config
.getSecUuid();
1558 if (configSecUuid
== null){
1561 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1562 return configSecUuid
.equals(taxonSecUuid
);
1566 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1569 public Synonym
findBestMatchingSynonym(String taxonName
) {
1570 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1571 if(! synonymList
.isEmpty()){
1572 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1573 if(synonymList
.size() == 1){
1574 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1577 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1586 * @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)
1589 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1590 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1592 Synonym synonym
= oldSynonymRelation
.getSynonym();
1593 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1594 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1595 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1596 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1597 //set default relationship type
1598 if (newSynonymRelationshipType
== null){
1599 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1601 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1603 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1604 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1605 boolean isSingleInGroup
= !(hgSize
> 1);
1607 if (! isSingleInGroup
){
1608 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1609 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1610 if (isHomotypicToAccepted
){
1611 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.";
1612 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1613 message
= String
.format(message
, homotypicRelatives
);
1614 throw new HomotypicalGroupChangeException(message
);
1616 if (! moveHomotypicGroup
){
1617 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.";
1618 throw new HomotypicalGroupChangeException(message
);
1621 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1623 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1625 SynonymRelationship result
= null;
1626 //move all synonyms to new taxon
1627 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1628 for (Synonym syn
: homotypicSynonyms
){
1629 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1630 for (SynonymRelationship synRelation
: synRelations
){
1631 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1632 Reference
<?
> newReference
= reference
;
1633 if (newReference
== null && keepReference
){
1634 newReference
= synRelation
.getCitation();
1636 String newRefDetail
= referenceDetail
;
1637 if (newRefDetail
== null && keepReference
){
1638 newRefDetail
= synRelation
.getCitationMicroReference();
1640 newTaxon
= HibernateProxyHelper
.deproxy(newTaxon
, Taxon
.class);
1641 fromTaxon
= HibernateProxyHelper
.deproxy(fromTaxon
, Taxon
.class);
1642 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1643 fromTaxon
.removeSynonymRelation(synRelation
, false);
1645 //change homotypic group of synonym if relType is 'homotypic'
1646 // if (newRelTypeIsHomotypic){
1647 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1650 if (synRelation
.equals(oldSynonymRelation
)){
1651 result
= newSynRelation
;
1657 saveOrUpdate(fromTaxon
);
1658 saveOrUpdate(newTaxon
);
1659 //Assert that there is a result
1660 if (result
== null){
1661 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1662 throw new IllegalStateException(message
);
1668 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1671 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1672 return dao
.getUuidAndTitleCacheTaxon();
1676 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1679 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1680 return dao
.getUuidAndTitleCacheSynonym();
1684 * @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)
1687 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1688 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1689 Classification classification
, List
<Language
> languages
,
1690 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1693 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1695 // --- execute search
1696 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1698 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1699 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1701 // --- initialize taxa, thighlight matches ....
1702 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1703 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1704 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1706 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1707 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1711 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTerm
> statusFilter
,
1712 Classification classification
,
1713 Integer pageSize
, Integer pageNumber
,
1714 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1716 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1718 // --- execute search
1719 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1721 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1722 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1724 // --- initialize taxa, thighlight matches ....
1725 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1726 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1727 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1729 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1730 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1735 * @param queryString
1736 * @param classification
1738 * @param highlightFragments
1739 * @param sortFields TODO
1740 * @param directorySelectClass
1743 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1744 boolean highlightFragments
, SortField
[] sortFields
) {
1745 BooleanQuery finalQuery
= new BooleanQuery();
1746 BooleanQuery textQuery
= new BooleanQuery();
1748 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1749 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1751 if(sortFields
== null){
1752 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1754 luceneSearch
.setSortFields(sortFields
);
1756 // ---- search criteria
1757 luceneSearch
.setCdmTypRestriction(clazz
);
1759 if(!queryString
.isEmpty() && !queryString
.equals("*") && !queryString
.equals("?") ) {
1760 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1761 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1764 if(textQuery
.getClauses().length
> 0) {
1765 finalQuery
.add(textQuery
, Occur
.MUST
);
1769 if(classification
!= null){
1770 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1772 luceneSearch
.setQuery(finalQuery
);
1774 if(highlightFragments
){
1775 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1777 return luceneSearch
;
1781 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1782 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1783 * drawback of requiring to do the join an indexing time.
1784 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1786 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1788 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1789 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1791 * @param queryString
1792 * @param classification
1794 * @param highlightFragments
1795 * @param sortFields TODO
1798 * @throws IOException
1800 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1801 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1804 String queryTermField
;
1805 String toField
= "id"; // TaxonBase.uuid
1807 if(edge
.isBidirectional()){
1808 throw new RuntimeException("Bidirectional joining not supported!");
1811 fromField
= "relatedFrom.id";
1812 queryTermField
= "relatedFrom.titleCache";
1813 } else if(edge
.isInvers()) {
1814 fromField
= "relatedTo.id";
1815 queryTermField
= "relatedTo.titleCache";
1817 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1820 BooleanQuery finalQuery
= new BooleanQuery();
1822 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1823 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1825 BooleanQuery joinFromQuery
= new BooleanQuery();
1826 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1827 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1828 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1830 if(sortFields
== null){
1831 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1833 luceneSearch
.setSortFields(sortFields
);
1835 finalQuery
.add(joinQuery
, Occur
.MUST
);
1837 if(classification
!= null){
1838 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1840 luceneSearch
.setQuery(finalQuery
);
1842 if(highlightFragments
){
1843 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1845 return luceneSearch
;
1852 * @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)
1855 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1856 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1857 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTerm
> distributionStatus
, List
<Language
> languages
,
1858 boolean highlightFragments
, Integer pageSize
,
1859 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1860 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1862 // FIXME: allow taxonomic ordering
1863 // 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";
1864 // this require building a special sort column by a special classBridge
1865 if(highlightFragments
){
1866 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1867 "currently not fully supported by this method and thus " +
1868 "may not work with common names and misapplied names.");
1871 // convert sets to lists
1872 List
<NamedArea
> namedAreaList
= null;
1873 List
<PresenceAbsenceTerm
>distributionStatusList
= null;
1874 if(namedAreas
!= null){
1875 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1876 namedAreaList
.addAll(namedAreas
);
1878 if(distributionStatus
!= null){
1879 distributionStatusList
= new ArrayList
<PresenceAbsenceTerm
>(distributionStatus
.size());
1880 distributionStatusList
.addAll(distributionStatus
);
1883 // set default if parameter is null
1884 if(searchModes
== null){
1885 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1888 // set sort order and thus override any sort orders which may have been
1889 // defindes by prepare*Search methods
1890 if(orderHints
== null){
1891 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1893 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1895 for(OrderHint oh
: orderHints
){
1896 sortFields
[i
++] = oh
.toSortField();
1898 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1899 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1902 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1904 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1905 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1908 ======== filtering by distribution , HOWTO ========
1910 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1911 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1912 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1913 which will be put into a FilteredQuersy in the end ?
1916 3. how does it work in spatial?
1918 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1919 - http://www.infoq.com/articles/LuceneSpatialSupport
1920 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1921 ------------------------------------------------------------------------
1924 A) use a separate distribution filter per index sub-query/search:
1925 - byTaxonSyonym (query TaxaonBase):
1926 use a join area filter (Distribution -> TaxonBase)
1927 - byCommonName (query DescriptionElementBase): use an area filter on
1928 DescriptionElementBase !!! PROBLEM !!!
1929 This cannot work since the distributions are different entities than the
1930 common names and thus these are different lucene documents.
1931 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1932 use a join area filter (Distribution -> TaxonBase)
1934 B) use a common distribution filter for all index sub-query/searches:
1935 - use a common join area filter (Distribution -> TaxonBase)
1936 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1937 PROBLEM in this case: we are losing the fragment highlighting for the
1938 common names, since the returned documents are always TaxonBases
1941 /* The QueryFactory for creating filter queries on Distributions should
1942 * The query factory used for the common names query cannot be reused
1943 * for this case, since we want to only record the text fields which are
1944 * actually used in the primary query
1946 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1948 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1951 // search for taxa or synonyms
1952 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1953 Class taxonBaseSubclass
= TaxonBase
.class;
1954 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1955 taxonBaseSubclass
= Taxon
.class;
1956 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1957 taxonBaseSubclass
= Synonym
.class;
1959 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1960 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1961 /* A) does not work!!!!
1962 if(addDistributionFilter){
1963 // in this case we need a filter which uses a join query
1964 // to get the TaxonBase documents for the DescriptionElementBase documents
1965 // which are matching the areas in question
1966 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1968 distributionStatusList,
1969 distributionFilterQueryFactory
1971 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1974 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1975 // add additional area filter for synonyms
1976 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1977 String toField
= "accTaxon.id"; // id in TaxonBase index
1979 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1981 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1982 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1987 // search by CommonTaxonName
1988 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1990 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1991 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1992 "inDescription.taxon.id",
1994 QueryFactory
.addTypeRestriction(
1995 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1996 , CommonTaxonName
.class
1998 CommonTaxonName
.class);
1999 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
2000 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2001 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
2002 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
2003 byCommonNameSearch
.setSortFields(sortFields
);
2004 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2006 luceneSearches
.add(byCommonNameSearch
);
2008 /* A) does not work!!!!
2010 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
2011 queryString, classification, null, languages, highlightFragments)
2013 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2014 if(addDistributionFilter){
2015 // in this case we are able to use DescriptionElementBase documents
2016 // which are matching the areas in question directly
2017 BooleanQuery byDistributionQuery = createByDistributionQuery(
2019 distributionStatusList,
2020 distributionFilterQueryFactory
2022 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2026 // search by misapplied names
2027 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
2029 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2030 // which allows doing query time joins
2031 // finds the misapplied name (Taxon B) which is an misapplication for
2032 // a related Taxon A.
2034 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
2035 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
2036 queryString
, classification
, languages
, highlightFragments
, sortFields
));
2037 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2039 if(addDistributionFilter
){
2040 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2043 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2044 * Maybe this is a but in java itself java.
2046 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2049 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2051 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2052 * will execute as expected:
2054 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2055 * String toField = "relation." + misappliedNameForUuid +".to.id";
2057 * Comparing both strings by the String.equals method returns true, so both String are identical.
2059 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2060 * 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)
2061 * The bug is persistent after a reboot of the development computer.
2063 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2064 // String toField = "relation." + misappliedNameForUuid +".to.id";
2065 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2066 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2067 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2069 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2070 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2071 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2073 // debug code for bug described above
2074 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2075 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2077 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2081 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2082 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2085 if(addDistributionFilter
){
2088 // in this case we need a filter which uses a join query
2089 // to get the TaxonBase documents for the DescriptionElementBase documents
2090 // which are matching the areas in question
2092 // for toTaxa, doByCommonName
2093 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2095 distributionStatusList
,
2096 distributionFilterQueryFactory
2098 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2101 if (addDistributionFilter
){
2102 multiSearch
.setFilter(multiIndexByAreaFilter
);
2106 // --- execute search
2107 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2109 // --- initialize taxa, highlight matches ....
2110 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2113 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2114 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2116 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2117 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2121 * @param namedAreaList at least one area must be in the list
2122 * @param distributionStatusList optional
2124 * @throws IOException
2126 protected Query
createByDistributionJoinQuery(
2127 List
<NamedArea
> namedAreaList
,
2128 List
<PresenceAbsenceTerm
> distributionStatusList
,
2129 QueryFactory queryFactory
2130 ) throws IOException
{
2132 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2133 String toField
= "id"; // id in TaxonBase index
2135 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2137 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2139 return taxonAreaJoinQuery
;
2143 * @param namedAreaList
2144 * @param distributionStatusList
2145 * @param queryFactory
2148 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2149 List
<PresenceAbsenceTerm
> distributionStatusList
, QueryFactory queryFactory
) {
2150 BooleanQuery areaQuery
= new BooleanQuery();
2151 // area field from Distribution
2152 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2154 // status field from Distribution
2155 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2156 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2159 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2164 * This method has been primarily created for testing the area join query but might
2165 * also be useful in other situations
2167 * @param namedAreaList
2168 * @param distributionStatusList
2169 * @param classification
2170 * @param highlightFragments
2172 * @throws IOException
2174 protected LuceneSearch
prepareByDistributionSearch(
2175 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTerm
> distributionStatusList
,
2176 Classification classification
) throws IOException
{
2178 BooleanQuery finalQuery
= new BooleanQuery();
2180 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2182 // FIXME is this query factory using the wrong type?
2183 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2185 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2186 luceneSearch
.setSortFields(sortFields
);
2189 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2191 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2193 if(classification
!= null){
2194 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2197 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2198 luceneSearch
.setQuery(finalQuery
);
2200 return luceneSearch
;
2206 * @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)
2209 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2210 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2211 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2212 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2215 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2217 // --- execute search
2218 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2220 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2221 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2223 // --- initialize taxa, highlight matches ....
2224 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2225 @SuppressWarnings("rawtypes")
2226 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2227 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2229 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2230 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2236 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2237 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2238 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2240 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2241 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2243 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2245 // --- execute search
2246 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2248 // --- initialize taxa, highlight matches ....
2249 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2251 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2252 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2253 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2255 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2256 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2258 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2259 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2266 * @param queryString
2267 * @param classification
2270 * @param highlightFragments
2271 * @param directorySelectClass
2274 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2275 String queryString
, Classification classification
, List
<Feature
> features
,
2276 List
<Language
> languages
, boolean highlightFragments
) {
2278 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2279 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2281 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2283 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2284 languages
, descriptionElementQueryFactory
);
2286 luceneSearch
.setSortFields(sortFields
);
2287 luceneSearch
.setCdmTypRestriction(clazz
);
2288 luceneSearch
.setQuery(finalQuery
);
2289 if(highlightFragments
){
2290 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2293 return luceneSearch
;
2297 * @param queryString
2298 * @param classification
2301 * @param descriptionElementQueryFactory
2304 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2305 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2306 BooleanQuery finalQuery
= new BooleanQuery();
2307 BooleanQuery textQuery
= new BooleanQuery();
2308 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2312 if(languages
== null || languages
.size() == 0){
2313 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2315 nameQuery
= new BooleanQuery();
2316 BooleanQuery languageSubQuery
= new BooleanQuery();
2317 for(Language lang
: languages
){
2318 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2320 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2321 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2323 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2326 // text field from TextData
2327 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2329 // --- TermBase fields - by representation ----
2330 // state field from CategoricalData
2331 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2333 // state field from CategoricalData
2334 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2336 // area field from Distribution
2337 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2339 // status field from Distribution
2340 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2342 finalQuery
.add(textQuery
, Occur
.MUST
);
2343 // --- classification ----
2345 if(classification
!= null){
2346 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2349 // --- IdentifieableEntity fields - by uuid
2350 if(features
!= null && features
.size() > 0 ){
2351 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2354 // the description must be associated with a taxon
2355 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2357 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2362 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2363 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2364 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2366 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2367 * or {@link MultilanguageTextFieldBridge }
2368 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2369 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2370 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2372 * TODO move to utiliy class !!!!!!!!
2374 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2376 if(stringBuilder
== null){
2377 stringBuilder
= new StringBuilder();
2379 if(languages
== null || languages
.size() == 0){
2380 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2382 for(Language lang
: languages
){
2383 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2386 return stringBuilder
;
2390 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2391 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2392 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2394 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2397 UUID nameUuid
= taxon
.getName().getUuid();
2398 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2399 String epithetOfTaxon
= null;
2400 String infragenericEpithetOfTaxon
= null;
2401 String infraspecificEpithetOfTaxon
= null;
2402 if (taxonName
.isSpecies()){
2403 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2404 } else if (taxonName
.isInfraGeneric()){
2405 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2406 } else if (taxonName
.isInfraSpecific()){
2407 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2409 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2410 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2411 List
<String
> taxonNames
= new ArrayList
<String
>();
2413 for (TaxonNode node
: nodes
){
2414 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2415 // List<String> synonymsEpithet = new ArrayList<String>();
2417 if (node
.getClassification().equals(classification
)){
2418 if (!node
.isTopmostNode()){
2419 TaxonNode parent
= node
.getParent();
2420 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2421 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2422 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2423 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2424 Rank rankOfTaxon
= taxonName
.getRank();
2427 //create inferred synonyms for species, subspecies
2428 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2430 Synonym inferredEpithet
= null;
2431 Synonym inferredGenus
= null;
2432 Synonym potentialCombination
= null;
2434 List
<String
> propertyPaths
= new ArrayList
<String
>();
2435 propertyPaths
.add("synonym");
2436 propertyPaths
.add("synonym.name");
2437 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2438 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2440 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2441 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2443 List
<TaxonRelationship
> taxonRelListParent
= null;
2444 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2445 if (doWithMisappliedNames
){
2446 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2447 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2451 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2454 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2455 Synonym syn
= synonymRelationOfParent
.getSynonym();
2457 inferredEpithet
= createInferredEpithets(taxon
,
2458 zooHashMap
, taxonName
, epithetOfTaxon
,
2459 infragenericEpithetOfTaxon
,
2460 infraspecificEpithetOfTaxon
,
2461 taxonNames
, parentName
,
2465 inferredSynonyms
.add(inferredEpithet
);
2466 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2467 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2470 if (doWithMisappliedNames
){
2472 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2473 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2475 inferredEpithet
= createInferredEpithets(taxon
,
2476 zooHashMap
, taxonName
, epithetOfTaxon
,
2477 infragenericEpithetOfTaxon
,
2478 infraspecificEpithetOfTaxon
,
2479 taxonNames
, parentName
,
2482 inferredSynonyms
.add(inferredEpithet
);
2483 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2484 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2488 if (!taxonNames
.isEmpty()){
2489 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2490 ZoologicalName name
;
2491 if (!synNotInCDM
.isEmpty()){
2492 inferredSynonymsToBeRemoved
.clear();
2494 for (Synonym syn
:inferredSynonyms
){
2495 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2496 if (!synNotInCDM
.contains(name
.getNameCache())){
2497 inferredSynonymsToBeRemoved
.add(syn
);
2501 // Remove identified Synonyms from inferredSynonyms
2502 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2503 inferredSynonyms
.remove(synonym
);
2508 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2511 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2512 TaxonNameBase synName
;
2513 ZoologicalName inferredSynName
;
2515 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2516 inferredGenus
= createInferredGenus(taxon
,
2517 zooHashMap
, taxonName
, epithetOfTaxon
,
2518 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2520 inferredSynonyms
.add(inferredGenus
);
2521 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2522 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2527 if (doWithMisappliedNames
){
2529 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2530 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2531 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2533 inferredSynonyms
.add(inferredGenus
);
2534 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2535 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2540 if (!taxonNames
.isEmpty()){
2541 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2542 ZoologicalName name
;
2543 if (!synNotInCDM
.isEmpty()){
2544 inferredSynonymsToBeRemoved
.clear();
2546 for (Synonym syn
:inferredSynonyms
){
2547 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2548 if (!synNotInCDM
.contains(name
.getNameCache())){
2549 inferredSynonymsToBeRemoved
.add(syn
);
2553 // Remove identified Synonyms from inferredSynonyms
2554 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2555 inferredSynonyms
.remove(synonym
);
2560 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2562 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2563 ZoologicalName inferredSynName
;
2564 //for all synonyms of the parent...
2565 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2566 TaxonNameBase synName
;
2567 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2568 synName
= synParent
.getName();
2570 HibernateProxyHelper
.deproxy(synParent
);
2572 // Set the sourceReference
2573 sourceReference
= synParent
.getSec();
2575 // Determine the idInSource
2576 String idInSourceParent
= getIdInSource(synParent
);
2578 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2579 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2580 String synParentInfragenericName
= null;
2581 String synParentSpecificEpithet
= null;
2583 if (parentSynZooName
.isInfraGeneric()){
2584 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2586 if (parentSynZooName
.isSpecies()){
2587 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2590 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2591 synonymsGenus.put(synGenusName, idInSource);
2594 //for all synonyms of the taxon
2596 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2598 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2599 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2600 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2602 synParentInfragenericName
,
2603 synParentSpecificEpithet
, syn
, zooHashMap
);
2605 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2606 inferredSynonyms
.add(potentialCombination
);
2607 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2608 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2615 if (doWithMisappliedNames
){
2617 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2619 TaxonNameBase misappliedParentName
;
2621 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2622 misappliedParentName
= misappliedParent
.getName();
2624 HibernateProxyHelper
.deproxy(misappliedParent
);
2626 // Set the sourceReference
2627 sourceReference
= misappliedParent
.getSec();
2629 // Determine the idInSource
2630 String idInSourceParent
= getIdInSource(misappliedParent
);
2632 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2633 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2634 String synParentInfragenericName
= null;
2635 String synParentSpecificEpithet
= null;
2637 if (parentSynZooName
.isInfraGeneric()){
2638 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2640 if (parentSynZooName
.isSpecies()){
2641 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2645 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2646 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2647 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2648 potentialCombination
= createPotentialCombination(
2649 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2651 synParentInfragenericName
,
2652 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2655 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2656 inferredSynonyms
.add(potentialCombination
);
2657 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2658 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2663 if (!taxonNames
.isEmpty()){
2664 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2665 ZoologicalName name
;
2666 if (!synNotInCDM
.isEmpty()){
2667 inferredSynonymsToBeRemoved
.clear();
2668 for (Synonym syn
:inferredSynonyms
){
2670 name
= (ZoologicalName
) syn
.getName();
2671 }catch (ClassCastException e
){
2672 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2674 if (!synNotInCDM
.contains(name
.getNameCache())){
2675 inferredSynonymsToBeRemoved
.add(syn
);
2678 // Remove identified Synonyms from inferredSynonyms
2679 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2680 inferredSynonyms
.remove(synonym
);
2686 logger
.info("The synonymrelationship type is not defined.");
2687 return inferredSynonyms
;
2694 return inferredSynonyms
;
2697 private Synonym
createPotentialCombination(String idInSourceParent
,
2698 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2699 String synParentInfragenericName
, String synParentSpecificEpithet
,
2700 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2701 Synonym potentialCombination
;
2702 Reference sourceReference
;
2703 ZoologicalName inferredSynName
;
2704 HibernateProxyHelper
.deproxy(syn
);
2706 // Set sourceReference
2707 sourceReference
= syn
.getSec();
2708 if (sourceReference
== null){
2709 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2711 if (!parentSynZooName
.getTaxa().isEmpty()){
2712 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2714 sourceReference
= taxon
.getSec();
2717 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2719 String synTaxonInfraSpecificName
= null;
2721 if (parentSynZooName
.isSpecies()){
2722 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2725 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2726 synonymsEpithet.add(epithetName);
2729 //create potential combinations...
2730 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2732 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2733 if (zooSynName
.isSpecies()){
2734 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2735 if (parentSynZooName
.isInfraGeneric()){
2736 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2739 if (zooSynName
.isInfraSpecific()){
2740 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2741 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2743 if (parentSynZooName
.isInfraGeneric()){
2744 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2748 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2750 // Set the sourceReference
2751 potentialCombination
.setSec(sourceReference
);
2754 // Determine the idInSource
2755 String idInSourceSyn
= getIdInSource(syn
);
2757 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2758 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2759 inferredSynName
.addSource(originalSource
);
2760 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2761 potentialCombination
.addSource(originalSource
);
2764 return potentialCombination
;
2767 private Synonym
createInferredGenus(Taxon taxon
,
2768 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2769 String epithetOfTaxon
, String genusOfTaxon
,
2770 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2773 Synonym inferredGenus
;
2774 TaxonNameBase synName
;
2775 ZoologicalName inferredSynName
;
2776 synName
=syn
.getName();
2777 HibernateProxyHelper
.deproxy(syn
);
2779 // Determine the idInSource
2780 String idInSourceSyn
= getIdInSource(syn
);
2781 String idInSourceTaxon
= getIdInSource(taxon
);
2782 // Determine the sourceReference
2783 Reference sourceReference
= syn
.getSec();
2785 //logger.warn(sourceReference.getTitleCache());
2787 synName
= syn
.getName();
2788 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2789 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2790 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2791 synonymsEpithet.add(synSpeciesEpithetName);
2794 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2795 //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...
2798 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2799 if (zooParentName
.isInfraGeneric()){
2800 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2803 if (taxonName
.isSpecies()){
2804 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2806 if (taxonName
.isInfraSpecific()){
2807 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2808 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2812 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2814 // Set the sourceReference
2815 inferredGenus
.setSec(sourceReference
);
2817 // Add the original source
2818 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2819 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2820 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2821 inferredGenus
.addSource(originalSource
);
2823 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2824 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2825 inferredSynName
.addSource(originalSource
);
2826 originalSource
= null;
2829 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2830 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2831 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2832 inferredGenus
.addSource(originalSource
);
2834 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2835 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2836 inferredSynName
.addSource(originalSource
);
2837 originalSource
= null;
2840 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2842 return inferredGenus
;
2845 private Synonym
createInferredEpithets(Taxon taxon
,
2846 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2847 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2848 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2849 TaxonNameBase parentName
, TaxonBase syn
) {
2851 Synonym inferredEpithet
;
2852 TaxonNameBase
<?
,?
> synName
;
2853 ZoologicalName inferredSynName
;
2854 HibernateProxyHelper
.deproxy(syn
);
2856 // Determine the idInSource
2857 String idInSourceSyn
= getIdInSource(syn
);
2858 String idInSourceTaxon
= getIdInSource(taxon
);
2859 // Determine the sourceReference
2860 Reference
<?
> sourceReference
= syn
.getSec();
2862 if (sourceReference
== null){
2863 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2864 sourceReference
= taxon
.getSec();
2867 synName
= syn
.getName();
2868 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2869 String synGenusName
= zooSynName
.getGenusOrUninomial();
2870 String synInfraGenericEpithet
= null;
2871 String synSpecificEpithet
= null;
2873 if (zooSynName
.getInfraGenericEpithet() != null){
2874 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2877 if (zooSynName
.isInfraSpecific()){
2878 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2881 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2882 synonymsGenus.put(synGenusName, idInSource);
2885 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2887 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2888 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2889 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2891 inferredSynName
.setGenusOrUninomial(synGenusName
);
2893 if (parentName
.isInfraGeneric()){
2894 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2896 if (taxonName
.isSpecies()){
2897 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2898 }else if (taxonName
.isInfraSpecific()){
2899 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2900 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2903 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2905 // Set the sourceReference
2906 inferredEpithet
.setSec(sourceReference
);
2908 /* Add the original source
2909 if (idInSource != null) {
2910 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2913 Reference citation = getCitation(syn);
2914 if (citation != null) {
2915 originalSource.setCitation(citation);
2916 inferredEpithet.addSource(originalSource);
2919 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2922 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2923 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2925 inferredEpithet
.addSource(originalSource
);
2927 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2928 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2930 inferredSynName
.addSource(originalSource
);
2934 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2936 return inferredEpithet
;
2940 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2941 * Very likely only useful for createInferredSynonyms().
2946 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2947 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2948 if (taxonName
== null) {
2949 taxonName
= zooHashMap
.get(uuid
);
2955 * Returns the idInSource for a given Synonym.
2958 private String
getIdInSource(TaxonBase taxonBase
) {
2959 String idInSource
= null;
2960 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2961 if (sources
.size() == 1) {
2962 IdentifiableSource source
= sources
.iterator().next();
2963 if (source
!= null) {
2964 idInSource
= source
.getIdInSource();
2966 } else if (sources
.size() > 1) {
2969 for (IdentifiableSource source
: sources
) {
2970 idInSource
+= source
.getIdInSource();
2971 if (count
< sources
.size()) {
2976 } else if (sources
.size() == 0){
2977 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2986 * Returns the citation for a given Synonym.
2989 private Reference
getCitation(Synonym syn
) {
2990 Reference citation
= null;
2991 Set
<IdentifiableSource
> sources
= syn
.getSources();
2992 if (sources
.size() == 1) {
2993 IdentifiableSource source
= sources
.iterator().next();
2994 if (source
!= null) {
2995 citation
= source
.getCitation();
2997 } else if (sources
.size() > 1) {
2998 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
3005 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
3006 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
3008 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
3009 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
3010 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
3012 return inferredSynonyms
;
3016 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
3018 // TODO quickly implemented, create according dao !!!!
3019 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
3020 Set
<Classification
> classifications
= new HashSet
<Classification
>();
3021 List
<Classification
> list
= new ArrayList
<Classification
>();
3023 if (taxonBase
== null) {
3027 taxonBase
= load(taxonBase
.getUuid());
3029 if (taxonBase
instanceof Taxon
) {
3030 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
3032 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
3033 nodes
.addAll(taxon
.getTaxonNodes());
3036 for (TaxonNode node
: nodes
) {
3037 classifications
.add(node
.getClassification());
3039 list
.addAll(classifications
);
3044 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3045 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3046 // Create new synonym using concept name
3047 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3048 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3050 // Remove concept relation from taxon
3051 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3056 // Create a new synonym for the taxon
3057 SynonymRelationship synonymRelationship
;
3058 if (synonymRelationshipType
!= null
3059 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3060 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3062 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3065 this.saveOrUpdate(toTaxon
);
3066 //TODO: configurator and classification
3067 TaxonDeletionConfigurator config
= new TaxonDeletionConfigurator();
3068 config
.setDeleteNameIfPossible(false);
3069 this.deleteTaxon(fromTaxon
, config
, null);
3070 return synonymRelationship
.getSynonym();
3074 public DeleteResult
isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3075 DeleteResult result
= new DeleteResult();
3076 Set
<CdmBase
> references
= commonService
.getReferencingObjectsForDeletion(taxonBase
);
3077 if (taxonBase
instanceof Taxon
){
3078 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3079 result
= isDeletableForTaxon(references
, taxonConfig
);
3081 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3082 result
= isDeletableForSynonym(references
, synonymConfig
);
3087 private DeleteResult
isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3089 DeleteResult result
= new DeleteResult();
3090 for (CdmBase ref
: references
){
3091 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3092 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3093 result
.addException(new ReferencedObjectUndeletableException(message
));
3094 result
.addRelatedObject(ref
);
3101 private DeleteResult
isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3102 String message
= null;
3103 DeleteResult result
= new DeleteResult();
3104 for (CdmBase ref
: references
){
3105 if (!(ref
instanceof TaxonNameBase
)){
3106 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3107 message
= "The Taxon can't be deleted as long as it has synonyms.";
3110 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3111 message
= "The Taxon can't be deleted as long as it has factual data.";
3115 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3116 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3119 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3120 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3121 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3124 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3128 if (ref
instanceof PolytomousKeyNode
){
3129 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3133 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3134 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3140 /* //PolytomousKeyNode
3141 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3142 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3147 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3148 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3153 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3154 message
= "Taxon can't be deleted as it is used in a determination event";
3159 if (message
!= null){
3160 result
.addException(new ReferencedObjectUndeletableException(message
));
3161 result
.addRelatedObject(ref
);
3170 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3171 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3173 //preliminary implementation
3175 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3176 TaxonBase taxonBase
= find(taxonUuid
);
3177 if (taxonBase
== null){
3178 return new IncludedTaxaDTO();
3179 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3180 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3182 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3183 //TODO partial synonyms ??
3184 //TODO synonyms in general
3185 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3186 taxa
.addAll(syn
.getAcceptedTaxa());
3188 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3191 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3193 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3194 related
= makeRelatedIncluded(related
, result
, config
);
3201 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3203 * @return the set of conceptually related taxa for further use
3206 * @param uncheckedTaxa
3207 * @param existingTaxa
3211 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3214 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3215 for (Taxon taxon
: uncheckedTaxa
){
3216 taxonNodes
.addAll(taxon
.getTaxonNodes());
3219 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3220 if (! config
.onlyCongruent
){
3221 for (TaxonNode node
: taxonNodes
){
3222 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3223 for (TaxonNode child
: childNodes
){
3224 children
.add(child
.getTaxon());
3227 children
.remove(null); // just to be on the save side
3230 Iterator
<Taxon
> it
= children
.iterator();
3231 while(it
.hasNext()){
3232 UUID uuid
= it
.next().getUuid();
3233 if (existingTaxa
.contains(uuid
)){
3236 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3241 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3242 uncheckedAndChildren
.addAll(children
);
3244 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3247 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3252 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3253 * @return the set of these computed taxa
3255 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3256 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3258 for (Taxon taxon
: unchecked
){
3259 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3260 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3262 for (TaxonRelationship fromRel
: fromRelations
){
3263 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3266 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3267 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3268 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3270 result
.add(fromRel
.getToTaxon());
3274 for (TaxonRelationship toRel
: toRelations
){
3275 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3278 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3279 result
.add(toRel
.getFromTaxon());
3284 Iterator
<Taxon
> it
= result
.iterator();
3285 while(it
.hasNext()){
3286 UUID uuid
= it
.next().getUuid();
3287 if (existingTaxa
.contains(uuid
)){
3290 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3297 public List
<TaxonBase
> findTaxaByName(MatchingTaxonConfigurator config
){
3298 List
<TaxonBase
> taxonList
= dao
.getTaxaByName(true, false, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, 0, config
.getPropertyPath());
3303 @Transactional(readOnly
= true)
3304 public <S
extends TaxonBase
> Pager
<FindByIdentifierDTO
<S
>> findByIdentifier(
3305 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
, TaxonNode subtreeFilter
,
3306 MatchMode matchmode
, boolean includeEntity
, Integer pageSize
,
3307 Integer pageNumber
, List
<String
> propertyPaths
) {
3308 if (subtreeFilter
== null){
3309 return findByIdentifier(clazz
, identifier
, identifierType
, matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3312 Integer numberOfResults
= dao
.countByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
, matchmode
);
3313 List
<Object
[]> daoResults
= new ArrayList
<Object
[]>();
3314 if(numberOfResults
> 0) { // no point checking again
3315 daoResults
= dao
.findByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
,
3316 matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3319 List
<FindByIdentifierDTO
<S
>> result
= new ArrayList
<FindByIdentifierDTO
<S
>>();
3320 for (Object
[] daoObj
: daoResults
){
3322 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (S
)daoObj
[2]));
3324 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
3327 return new DefaultPagerImpl
<FindByIdentifierDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);
3331 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, UUID newTaxonUUID
, boolean moveHomotypicGroup
,
3332 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
3334 Taxon newTaxon
= (Taxon
) dao
.load(newTaxonUUID
);
3335 return moveSynonymToAnotherTaxon(oldSynonymRelation
, newTaxon
, moveHomotypicGroup
, newSynonymRelationshipType
, reference
, referenceDetail
, keepReference
);