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 @Transactional(readOnly
= false)
261 public UpdateResult
swapSynonymAndAcceptedTaxon(UUID synonymUuid
, UUID acceptedTaxonUuid
) {
262 UpdateResult result
= new UpdateResult();
263 Synonym synonym
= (Synonym
)dao
.load(synonymUuid
);
264 Taxon taxon
= (Taxon
)dao
.load(acceptedTaxonUuid
);
265 result
.addUpdatedObject(synonym
);
266 result
.addUpdatedObject(taxon
);
267 swapSynonymAndAcceptedTaxon(synonym
, taxon
);
272 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
276 @Transactional(readOnly
= false)
277 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
279 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
280 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
281 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
283 //check synonym is not homotypic
284 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
285 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
286 throw new HomotypicalGroupChangeException(message
);
289 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
291 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
292 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
293 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
295 for (Synonym heteroSynonym
: heteroSynonyms
){
296 if (synonym
.equals(heteroSynonym
)){
297 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
300 //move synonyms in same homotypic group to new accepted taxon
301 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
305 //synonym.getName().removeTaxonBase(synonym);
308 // deleteSynonym(synonym, taxon, false);
311 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
312 config
.setDeleteNameIfPossible(false);
313 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
315 } catch (Exception e
) {
316 logger
.info("Can't delete old synonym from database");
320 return newAcceptedTaxon
;
325 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
327 // Get name from synonym
328 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
330 /* // remove synonym from taxon
331 toTaxon.removeSynonym(synonym);
333 // Create a taxon with synonym name
334 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
336 // Add taxon relation
337 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
339 // since we are swapping names, we have to detach the name from the synonym completely.
340 // Otherwise the synonym will still be in the list of typified names.
341 // synonym.getName().removeTaxonBase(synonym);
342 this.deleteSynonym(synonym
, null);
349 * @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)
351 @Transactional(readOnly
= false)
353 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
354 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
356 TaxonNameBase synonymName
= synonym
.getName();
357 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
361 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
362 newHomotypicalGroup
.addTypifiedName(synonymName
);
364 //remove existing basionym relationships
365 synonymName
.removeBasionyms();
367 //add basionym relationship
368 if (setBasionymRelationIfApplicable
){
369 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
370 for (TaxonNameBase basionym
: basionyms
){
371 synonymName
.addBasionym(basionym
);
375 //set synonym relationship correctly
376 // SynonymRelationship relToTaxon = null;
377 boolean relToTargetTaxonExists
= false;
378 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
379 for (SynonymRelationship rel
: existingRelations
){
380 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
381 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
382 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
383 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
384 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
385 rel
.setType(newRelationType
);
386 //TODO handle citation and microCitation
389 relToTargetTaxonExists
= true;
391 if (removeFromOtherTaxa
){
392 acceptedTaxon
.removeSynonym(synonym
, false);
398 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
399 Taxon acceptedTaxon
= targetTaxon
;
400 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
401 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
402 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
403 //TODO handle citation and microCitation
404 Reference citation
= null;
405 String microCitation
= null;
406 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
413 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
416 @Transactional(readOnly
= false)
417 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
419 clazz
= TaxonBase
.class;
421 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
426 protected void setDao(ITaxonDao dao
) {
431 public Pager
<TaxonBase
> findTaxaByName(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
);
439 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
443 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
444 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
446 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
447 if(numberOfResults
> 0) { // no point checking again
448 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
455 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
456 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
458 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
459 if(numberOfResults
> 0) { // no point checking again
460 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
466 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
467 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
469 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
470 if(numberOfResults
> 0) { // no point checking again
471 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
473 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
477 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
478 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
480 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
481 if(numberOfResults
> 0) { // no point checking again
482 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
488 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
489 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
491 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
492 if(numberOfResults
> 0) { // no point checking again
493 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
495 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
499 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
500 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
501 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
505 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
506 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
508 List
<Taxon
> list
= new ArrayList
<Taxon
>();
511 Synonym synonym
= null;
514 synonym
= (Synonym
) dao
.load(synonymUuid
);
515 } catch (ClassCastException e
){
516 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
517 } catch (NullPointerException e
){
518 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
521 Classification classificationFilter
= null;
522 if(classificationUuid
!= null){
524 classificationFilter
= classificationDao
.load(classificationUuid
);
525 } catch (NullPointerException e
){
526 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
528 if(classificationFilter
== null){
533 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
534 if(count
> (pageSize
* pageNumber
)){
535 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
538 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
543 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
544 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
546 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
547 relatedTaxa
.remove(taxon
);
548 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
554 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
555 * <code>taxon</code> supplied as parameter.
558 * @param includeRelationships
560 * @param maxDepth can be <code>null</code> for infinite depth
563 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
569 if(includeRelationships
.isEmpty()){
573 if(maxDepth
!= null) {
576 if(logger
.isDebugEnabled()){
577 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
579 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
580 for (TaxonRelationship taxRel
: taxonRelationships
) {
583 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
586 // filter by includeRelationships
587 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
588 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
589 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
590 if(logger
.isDebugEnabled()){
591 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
593 taxa
.add(taxRel
.getToTaxon());
594 if(maxDepth
== null || maxDepth
> 0) {
595 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
598 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
599 taxa
.add(taxRel
.getFromTaxon());
600 if(logger
.isDebugEnabled()){
601 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
603 if(maxDepth
== null || maxDepth
> 0) {
604 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
614 * @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)
617 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
618 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
620 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
621 if(numberOfResults
> 0) { // no point checking again
622 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
625 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
629 * @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)
632 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
633 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
635 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
636 if(numberOfResults
> 0) { // no point checking again
637 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
640 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
644 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
647 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
648 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
649 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
652 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
655 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
656 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
657 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
665 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
668 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
669 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
670 return t
.getHomotypicSynonymsByHomotypicGroup();
674 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
677 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
678 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
679 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
680 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
681 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
682 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
684 return heterotypicSynonymyGroups
;
688 public List
<UuidAndTitleCache
<IdentifiableEntity
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
690 List
<UuidAndTitleCache
<IdentifiableEntity
>> results
= new ArrayList
<UuidAndTitleCache
<IdentifiableEntity
>>();
693 if (configurator
.isDoSynonyms() || configurator
.isDoTaxa() || configurator
.isDoNamesWithoutTaxa()){
694 results
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.isDoNamesWithoutTaxa(), configurator
.isDoMisappliedNames(),configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
696 if (configurator
.isDoTaxaByCommonNames()) {
698 //if(configurator.getPageSize() == null ){
699 List
<UuidAndTitleCache
<IdentifiableEntity
>> commonNameResults
= dao
.getTaxaByCommonNameForEditor(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
700 if(commonNameResults
!= null){
701 results
.addAll(commonNameResults
);
709 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
712 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
714 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
715 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
716 List
<TaxonBase
> taxa
= null;
719 long numberTaxaResults
= 0L;
722 List
<String
> propertyPath
= new ArrayList
<String
>();
723 if(configurator
.getTaxonPropertyPath() != null){
724 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
728 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
729 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
731 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
732 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
733 configurator
.getNamedAreas());
736 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
737 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
738 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
739 configurator
.getMatchMode(), configurator
.getNamedAreas(),
740 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
744 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
747 results
.addAll(taxa
);
750 numberOfResults
+= numberTaxaResults
;
752 // Names without taxa
753 if (configurator
.isDoNamesWithoutTaxa()) {
754 int numberNameResults
= 0;
756 List
<?
extends TaxonNameBase
<?
,?
>> names
=
757 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
758 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
759 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
760 if (names
.size() > 0) {
761 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
762 if (taxonName
.getTaxonBases().size() == 0) {
763 results
.add(taxonName
);
767 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
768 numberOfResults
+= numberNameResults
;
772 // Taxa from common names
774 if (configurator
.isDoTaxaByCommonNames()) {
775 taxa
= new ArrayList
<TaxonBase
>();
776 numberTaxaResults
= 0;
777 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
778 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
780 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
781 List
<Taxon
> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
782 taxa
.addAll(commonNameResults
);
785 results
.addAll(taxa
);
787 numberOfResults
+= numberTaxaResults
;
791 return new DefaultPagerImpl
<IdentifiableEntity
>
792 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
795 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
796 return dao
.getUuidAndTitleCache();
800 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
803 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
804 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
805 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
806 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
807 for (TaxonDescription taxDesc
: descriptions
){
808 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
809 for (DescriptionElementBase descElem
: elements
){
810 for(Media media
: descElem
.getMedia()){
812 //find the best matching representation
813 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
822 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
825 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
826 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
831 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
834 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
835 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
836 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
838 logger
.trace("listMedia() - START");
840 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
841 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
842 List
<Media
> nonImageGalleryImages
= new ArrayList
<Media
>();
844 if (limitToGalleries
== null) {
845 limitToGalleries
= false;
848 // --- resolve related taxa
849 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
850 logger
.trace("listMedia() - resolve related taxa");
851 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
854 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
856 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
857 logger
.trace("listMedia() - includeTaxonDescriptions");
858 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
859 // --- TaxonDescriptions
860 for (Taxon t
: taxa
) {
861 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
863 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
864 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
865 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
866 for (Media media
: element
.getMedia()) {
867 if(taxonDescription
.isImageGallery()){
868 taxonMedia
.add(media
);
871 nonImageGalleryImages
.add(media
);
877 //put images from image gallery first (#3242)
878 taxonMedia
.addAll(nonImageGalleryImages
);
882 if(includeOccurrences
!= null && includeOccurrences
) {
883 logger
.trace("listMedia() - includeOccurrences");
884 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
886 for (Taxon t
: taxa
) {
887 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
889 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
891 // direct media removed from specimen #3597
892 // taxonMedia.addAll(occurrence.getMedia());
894 // SpecimenDescriptions
895 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
896 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
897 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
898 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
899 for (DescriptionElementBase element
: elements
) {
900 for (Media media
: element
.getMedia()) {
901 taxonMedia
.add(media
);
908 //TODO why may collections have media attached? #
909 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
910 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
911 if (derivedUnit
.getCollection() != null){
912 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
916 // pherograms & gelPhotos
917 if (occurrence
.isInstanceOf(DnaSample
.class)) {
918 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
919 Set
<Sequence
> sequences
= dnaSample
.getSequences();
920 //we do show only those gelPhotos which lead to a consensus sequence
921 for (Sequence sequence
: sequences
) {
922 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
923 for (SingleRead singleRead
: sequence
.getSingleReads()){
924 AmplificationResult amplification
= singleRead
.getAmplificationResult();
925 dnaRelatedMedia
.add(amplification
.getGelPhoto());
926 dnaRelatedMedia
.add(singleRead
.getPherogram());
927 dnaRelatedMedia
.remove(null);
929 taxonMedia
.addAll(dnaRelatedMedia
);
936 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
937 logger
.trace("listMedia() - includeTaxonNameDescriptions");
938 // --- TaxonNameDescription
939 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
940 for (Taxon t
: taxa
) {
941 nameDescriptions
.addAll(t
.getName().getDescriptions());
943 for(TaxonNameDescription nameDescription
: nameDescriptions
){
944 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
945 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
946 for (DescriptionElementBase element
: elements
) {
947 for (Media media
: element
.getMedia()) {
948 taxonMedia
.add(media
);
956 logger
.trace("listMedia() - initialize");
957 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
959 logger
.trace("listMedia() - END");
965 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
968 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
969 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
973 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
976 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
977 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
981 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
984 public int countAllRelationships() {
985 return this.dao
.countAllRelationships();
992 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
995 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
996 return this.dao
.findIdenticalTaxonNames(propertyPath
);
1000 public DeleteResult
deleteTaxon(UUID taxonUuid
, TaxonDeletionConfigurator config
, UUID classificationUuid
) {
1001 return deleteTaxon((Taxon
)dao
.load(taxonUuid
), config
, classificationDao
.load(classificationUuid
));
1004 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1007 public DeleteResult
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
1009 if (config
== null){
1010 config
= new TaxonDeletionConfigurator();
1013 DeleteResult result
= isDeletable(taxon
, config
);
1016 // --- DeleteSynonymRelations
1017 if (config
.isDeleteSynonymRelations()){
1018 boolean removeSynonymNameFromHomotypicalGroup
= false;
1019 // use tmp Set to avoid concurrent modification
1020 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1021 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1022 for (SynonymRelationship synRel
: synRelsToDelete
){
1023 Synonym synonym
= synRel
.getSynonym();
1024 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1025 // this will cause hibernate to delete the relationship since
1026 // the SynonymRelationship field on both is annotated with removeOrphan
1027 // so no further explicit deleting of the relationship should be done here
1028 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1030 // --- DeleteSynonymsIfPossible
1031 if (config
.isDeleteSynonymsIfPossible()){
1033 boolean newHomotypicGroupIfNeeded
= true;
1034 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1035 deleteSynonym(synonym
, taxon
, synConfig
);
1037 // relationship will be deleted by hibernate automatically,
1038 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1040 // deleteSynonymRelationships(synonym, taxon);
1045 // --- DeleteTaxonRelationships
1046 if (! config
.isDeleteTaxonRelationships()){
1047 if (taxon
.getTaxonRelations().size() > 0){
1048 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1049 "Remove taxon from all relations to other taxa prior to deletion.";
1050 // throw new ReferencedObjectUndeletableException(message);
1053 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1054 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1055 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1056 if (taxon
.equals(taxRel
.getToTaxon())){
1057 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1061 taxon
.removeTaxonRelation(taxRel
);
1062 /*if (taxFrom.equals(taxon)){
1064 this.deleteTaxon(taxTo, taxConf, classification);
1065 } catch(DataChangeNoRollbackException e){
1066 logger.debug("A related taxon will not be deleted." + e.getMessage());
1070 this.deleteTaxon(taxFrom, taxConf, classification);
1071 } catch(DataChangeNoRollbackException e){
1072 logger.debug("A related taxon will not be deleted." + e.getMessage());
1080 if (config
.isDeleteDescriptions()){
1081 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1082 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1083 for (TaxonDescription desc
: descriptions
){
1084 //TODO use description delete configurator ?
1085 //FIXME check if description is ALWAYS deletable
1086 if (desc
.getDescribedSpecimenOrObservation() != null){
1087 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1088 " which also describes specimens or abservations";
1089 //throw new ReferencedObjectUndeletableException(message);
1092 removeDescriptions
.add(desc
);
1096 for (TaxonDescription desc
: removeDescriptions
){
1097 taxon
.removeDescription(desc
);
1098 descriptionService
.delete(desc
);
1103 /* //check references with only reverse mapping
1104 String message = checkForReferences(taxon);
1105 if (message != null){
1106 //throw new ReferencedObjectUndeletableException(message.toString());
1109 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1110 //if (taxon.getTaxonNodes().size() > 0){
1111 // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";
1112 // throw new ReferencedObjectUndeletableException(message);
1115 if (taxon
.getTaxonNodes().size() != 0){
1116 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1117 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1118 TaxonNode node
= null;
1119 boolean deleteChildren
;
1120 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1121 deleteChildren
= true;
1123 deleteChildren
= false;
1125 boolean success
= true;
1126 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1127 while (iterator
.hasNext()){
1128 node
= iterator
.next();
1129 if (node
.getClassification().equals(classification
)){
1135 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1136 nodeService
.delete(node
);
1139 result
.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
1141 } else if (config
.isDeleteInAllClassifications()){
1142 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1143 nodesList
.addAll(taxon
.getTaxonNodes());
1145 for (ITaxonTreeNode treeNode
: nodesList
){
1146 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1147 if(!deleteChildren
){
1148 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1149 //nodesList.addAll(taxonNode.getChildNodes());
1150 for (Object childNode: childNodes){
1151 TaxonNode childNodeCast = (TaxonNode) childNode;
1152 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1156 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1157 deleteTaxon(childNode.getTaxon(), config, classification);
1160 // taxon.removeTaxonNode(taxonNode);
1161 //nodeService.delete(taxonNode);
1164 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1165 for (Object childNode
: childNodes
){
1166 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1167 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1170 //taxon.removeTaxonNode(taxonNode);
1173 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1174 DeleteResult resultNodes
= nodeService
.deleteTaxonNodes(nodesList
, config
);
1175 if (!resultNodes
.isOk()){
1176 result
.addExceptions(resultNodes
.getExceptions());
1177 result
.setStatus(resultNodes
.getStatus());
1182 result
.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
1188 //PolytomousKey TODO
1192 if (config
.isDeleteNameIfPossible()){
1195 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1196 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1197 //check whether taxon will be deleted or not
1198 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1199 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1200 //name.removeTaxonBase(taxon);
1201 //nameService.saveOrUpdate(name);
1202 taxon
.setName(null);
1203 //dao.delete(taxon);
1204 DeleteResult nameResult
= new DeleteResult();
1206 //remove name if possible (and required)
1207 if (name
!= null && config
.isDeleteNameIfPossible()){
1209 nameResult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1215 if (nameResult
.isError()){
1216 //result.setError();
1217 result
.addRelatedObject(name
);
1218 result
.addExceptions(nameResult
.getExceptions());
1226 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1228 for (TaxonDescription desc: descriptions){
1229 if (config.isDeleteDescriptions()){
1230 //TODO use description delete configurator ?
1231 //FIXME check if description is ALWAYS deletable
1232 taxon.removeDescription(desc);
1233 descriptionService.delete(desc);
1235 if (desc.getDescribedSpecimenOrObservations().size()>0){
1236 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1237 " which also describes specimens or observations";
1238 throw new ReferencedObjectUndeletableException(message);
1243 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1245 UUID uuid
= dao
.delete(taxon
);
1247 }catch(Exception e
){
1248 result
.addException(e
);
1254 result
.addException(new Exception("The Taxon can't be deleted."));
1259 // List<Exception> exceptions = new ArrayList<Exception>();
1260 // for (String message: referencedObjects){
1261 // ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message);
1262 // exceptions.add(exception);
1264 // result.addExceptions(exceptions);
1265 // result.setError();
1272 private String
checkForReferences(Taxon taxon
){
1273 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1274 for (CdmBase referencingObject
: referencingObjects
){
1275 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1276 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1277 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";
1283 /* //PolytomousKeyNode
1284 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1285 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1290 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1291 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1296 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1297 String message
= "Taxon can't be deleted as it is used in a determination event";
1303 referencingObjects
= null;
1307 private boolean checkForPolytomousKeys(Taxon taxon
){
1308 boolean result
= false;
1309 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1310 if (!list
.isEmpty()) {
1316 @Transactional(readOnly
= false)
1317 public UUID
delete(Synonym syn
){
1318 UUID result
= syn
.getUuid();
1319 this.deleteSynonym(syn
, null);
1326 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1328 @Transactional(readOnly
= false)
1330 public DeleteResult
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1331 return deleteSynonym(synonym
, null, config
);
1337 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1339 @Transactional(readOnly
= false)
1341 public DeleteResult
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1342 DeleteResult result
= new DeleteResult();
1343 if (synonym
== null){
1348 if (config
== null){
1349 config
= new SynonymDeletionConfigurator();
1351 result
= isDeletable(synonym
, config
);
1355 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1357 //remove synonymRelationship
1358 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1360 taxonSet
.add(taxon
);
1362 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1364 for (Taxon relatedTaxon
: taxonSet
){
1365 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1366 relatedTaxon
.removeSynonym(synonym
, false);
1367 this.saveOrUpdate(relatedTaxon
);
1369 this.saveOrUpdate(synonym
);
1371 //TODO remove name from homotypical group?
1373 //remove synonym (if necessary)
1376 if (synonym
.getSynonymRelations().isEmpty()){
1377 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1378 synonym
.setName(null);
1379 uuid
= dao
.delete(synonym
);
1381 //remove name if possible (and required)
1382 if (name
!= null && config
.isDeleteNameIfPossible()){
1384 DeleteResult nameDeleteresult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1385 if (nameDeleteresult
.isAbort()){
1386 result
.addExceptions(nameDeleteresult
.getExceptions());
1387 result
.addUpdatedObject(name
);
1394 result
.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
1402 // List<Exception> exceptions = new ArrayList<Exception>();
1403 // for (String message :messages){
1404 // exceptions.add(new ReferencedObjectUndeletableException(message));
1406 // result.setError();
1407 // result.addExceptions(exceptions);
1414 @Transactional(readOnly
= false)
1416 public DeleteResult
deleteSynonym(UUID synonymUuid
, UUID taxonUuid
, SynonymDeletionConfigurator config
) {
1417 return deleteSynonym((Synonym
)dao
.load(synonymUuid
), (Taxon
)dao
.load(taxonUuid
), config
);
1421 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1424 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1426 return this.dao
.findIdenticalNamesNew(propertyPath
);
1430 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1433 public String
getPhylumName(TaxonNameBase name
){
1434 return this.dao
.getPhylumName(name
);
1438 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1441 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1442 return dao
.deleteSynonymRelationships(syn
, taxon
);
1446 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1449 public long deleteSynonymRelationships(Synonym syn
) {
1450 return dao
.deleteSynonymRelationships(syn
, null);
1455 * @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)
1458 public List
<SynonymRelationship
> listSynonymRelationships(
1459 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1460 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1461 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1463 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1464 if(numberOfResults
> 0) { // no point checking again
1465 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1471 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1474 public Taxon
findBestMatchingTaxon(String taxonName
) {
1475 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1476 config
.setTaxonNameTitle(taxonName
);
1477 return findBestMatchingTaxon(config
);
1483 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1485 Taxon bestCandidate
= null;
1487 // 1. search for acceptet taxa
1488 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1489 boolean bestCandidateMatchesSecUuid
= false;
1490 boolean bestCandidateIsInClassification
= false;
1491 int countEqualCandidates
= 0;
1492 for(TaxonBase taxonBaseCandidate
: taxonList
){
1493 if(taxonBaseCandidate
instanceof Taxon
){
1494 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1495 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1496 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1498 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1499 bestCandidate
= newCanditate
;
1500 countEqualCandidates
= 1;
1501 bestCandidateMatchesSecUuid
= true;
1505 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1506 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1508 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1509 bestCandidate
= newCanditate
;
1510 countEqualCandidates
= 1;
1511 bestCandidateIsInClassification
= true;
1514 if (bestCandidate
== null){
1515 bestCandidate
= newCanditate
;
1516 countEqualCandidates
= 1;
1520 }else{ //not Taxon.class
1523 countEqualCandidates
++;
1526 if (bestCandidate
!= null){
1527 if(countEqualCandidates
> 1){
1528 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1529 return bestCandidate
;
1531 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1532 return bestCandidate
;
1537 // 2. search for synonyms
1538 if (config
.isIncludeSynonyms()){
1539 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1540 for(TaxonBase taxonBase
: synonymList
){
1541 if(taxonBase
instanceof Synonym
){
1542 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1543 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1544 if(!acceptetdCandidates
.isEmpty()){
1545 bestCandidate
= acceptetdCandidates
.iterator().next();
1546 if(acceptetdCandidates
.size() == 1){
1547 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1548 return bestCandidate
;
1550 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1551 return bestCandidate
;
1553 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1559 } catch (Exception e
){
1561 e
.printStackTrace();
1564 return bestCandidate
;
1567 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1568 UUID configClassificationUuid
= config
.getClassificationUuid();
1569 if (configClassificationUuid
== null){
1572 for (TaxonNode node
: taxon
.getTaxonNodes()){
1573 UUID classUuid
= node
.getClassification().getUuid();
1574 if (configClassificationUuid
.equals(classUuid
)){
1581 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1582 UUID configSecUuid
= config
.getSecUuid();
1583 if (configSecUuid
== null){
1586 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1587 return configSecUuid
.equals(taxonSecUuid
);
1591 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1594 public Synonym
findBestMatchingSynonym(String taxonName
) {
1595 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1596 if(! synonymList
.isEmpty()){
1597 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1598 if(synonymList
.size() == 1){
1599 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1602 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1611 * @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)
1614 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1615 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1617 Synonym synonym
= oldSynonymRelation
.getSynonym();
1618 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1619 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1620 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1621 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1622 //set default relationship type
1623 if (newSynonymRelationshipType
== null){
1624 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1626 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1628 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1629 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1630 boolean isSingleInGroup
= !(hgSize
> 1);
1632 if (! isSingleInGroup
){
1633 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1634 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1635 if (isHomotypicToAccepted
){
1636 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.";
1637 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1638 message
= String
.format(message
, homotypicRelatives
);
1639 throw new HomotypicalGroupChangeException(message
);
1641 if (! moveHomotypicGroup
){
1642 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.";
1643 throw new HomotypicalGroupChangeException(message
);
1646 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1648 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1650 SynonymRelationship result
= null;
1651 //move all synonyms to new taxon
1652 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1653 for (Synonym syn
: homotypicSynonyms
){
1654 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1655 for (SynonymRelationship synRelation
: synRelations
){
1656 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1657 Reference
<?
> newReference
= reference
;
1658 if (newReference
== null && keepReference
){
1659 newReference
= synRelation
.getCitation();
1661 String newRefDetail
= referenceDetail
;
1662 if (newRefDetail
== null && keepReference
){
1663 newRefDetail
= synRelation
.getCitationMicroReference();
1665 newTaxon
= HibernateProxyHelper
.deproxy(newTaxon
, Taxon
.class);
1666 fromTaxon
= HibernateProxyHelper
.deproxy(fromTaxon
, Taxon
.class);
1667 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1668 fromTaxon
.removeSynonymRelation(synRelation
, false);
1670 //change homotypic group of synonym if relType is 'homotypic'
1671 // if (newRelTypeIsHomotypic){
1672 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1675 if (synRelation
.equals(oldSynonymRelation
)){
1676 result
= newSynRelation
;
1682 saveOrUpdate(fromTaxon
);
1683 saveOrUpdate(newTaxon
);
1684 //Assert that there is a result
1685 if (result
== null){
1686 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1687 throw new IllegalStateException(message
);
1693 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1696 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1697 return dao
.getUuidAndTitleCacheTaxon();
1701 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1704 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1705 return dao
.getUuidAndTitleCacheSynonym();
1709 * @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)
1712 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1713 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1714 Classification classification
, List
<Language
> languages
,
1715 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1718 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1720 // --- execute search
1721 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1723 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1724 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1726 // --- initialize taxa, thighlight matches ....
1727 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1728 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1729 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1731 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1732 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1736 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTerm
> statusFilter
,
1737 Classification classification
,
1738 Integer pageSize
, Integer pageNumber
,
1739 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1741 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1743 // --- execute search
1744 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1746 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1747 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1749 // --- initialize taxa, thighlight matches ....
1750 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1751 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1752 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1754 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1755 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1760 * @param queryString
1761 * @param classification
1763 * @param highlightFragments
1764 * @param sortFields TODO
1765 * @param directorySelectClass
1768 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1769 boolean highlightFragments
, SortField
[] sortFields
) {
1770 BooleanQuery finalQuery
= new BooleanQuery();
1771 BooleanQuery textQuery
= new BooleanQuery();
1773 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1774 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1776 if(sortFields
== null){
1777 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1779 luceneSearch
.setSortFields(sortFields
);
1781 // ---- search criteria
1782 luceneSearch
.setCdmTypRestriction(clazz
);
1784 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1785 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1787 finalQuery
.add(textQuery
, Occur
.MUST
);
1789 if(classification
!= null){
1790 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1792 luceneSearch
.setQuery(finalQuery
);
1794 if(highlightFragments
){
1795 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1797 return luceneSearch
;
1801 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1802 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1803 * drawback of requiring to do the join an indexing time.
1804 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1806 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1808 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1809 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1811 * @param queryString
1812 * @param classification
1814 * @param highlightFragments
1815 * @param sortFields TODO
1818 * @throws IOException
1820 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1821 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1824 String queryTermField
;
1825 String toField
= "id"; // TaxonBase.uuid
1827 if(edge
.isBidirectional()){
1828 throw new RuntimeException("Bidirectional joining not supported!");
1831 fromField
= "relatedFrom.id";
1832 queryTermField
= "relatedFrom.titleCache";
1833 } else if(edge
.isInvers()) {
1834 fromField
= "relatedTo.id";
1835 queryTermField
= "relatedTo.titleCache";
1837 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1840 BooleanQuery finalQuery
= new BooleanQuery();
1842 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1843 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1845 BooleanQuery joinFromQuery
= new BooleanQuery();
1846 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1847 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1848 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1850 if(sortFields
== null){
1851 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1853 luceneSearch
.setSortFields(sortFields
);
1855 finalQuery
.add(joinQuery
, Occur
.MUST
);
1857 if(classification
!= null){
1858 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1860 luceneSearch
.setQuery(finalQuery
);
1862 if(highlightFragments
){
1863 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1865 return luceneSearch
;
1872 * @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)
1875 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1876 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1877 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTerm
> distributionStatus
, List
<Language
> languages
,
1878 boolean highlightFragments
, Integer pageSize
,
1879 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1880 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1882 // FIXME: allow taxonomic ordering
1883 // 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";
1884 // this require building a special sort column by a special classBridge
1885 if(highlightFragments
){
1886 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1887 "currently not fully supported by this method and thus " +
1888 "may not work with common names and misapplied names.");
1891 // convert sets to lists
1892 List
<NamedArea
> namedAreaList
= null;
1893 List
<PresenceAbsenceTerm
>distributionStatusList
= null;
1894 if(namedAreas
!= null){
1895 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1896 namedAreaList
.addAll(namedAreas
);
1898 if(distributionStatus
!= null){
1899 distributionStatusList
= new ArrayList
<PresenceAbsenceTerm
>(distributionStatus
.size());
1900 distributionStatusList
.addAll(distributionStatus
);
1903 // set default if parameter is null
1904 if(searchModes
== null){
1905 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1908 // set sort order and thus override any sort orders which may have been
1909 // defindes by prepare*Search methods
1910 if(orderHints
== null){
1911 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1913 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1915 for(OrderHint oh
: orderHints
){
1916 sortFields
[i
++] = oh
.toSortField();
1918 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1919 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1922 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1924 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1925 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1928 ======== filtering by distribution , HOWTO ========
1930 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1931 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1932 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1933 which will be put into a FilteredQuersy in the end ?
1936 3. how does it work in spatial?
1938 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1939 - http://www.infoq.com/articles/LuceneSpatialSupport
1940 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1941 ------------------------------------------------------------------------
1944 A) use a separate distribution filter per index sub-query/search:
1945 - byTaxonSyonym (query TaxaonBase):
1946 use a join area filter (Distribution -> TaxonBase)
1947 - byCommonName (query DescriptionElementBase): use an area filter on
1948 DescriptionElementBase !!! PROBLEM !!!
1949 This cannot work since the distributions are different entities than the
1950 common names and thus these are different lucene documents.
1951 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1952 use a join area filter (Distribution -> TaxonBase)
1954 B) use a common distribution filter for all index sub-query/searches:
1955 - use a common join area filter (Distribution -> TaxonBase)
1956 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1957 PROBLEM in this case: we are losing the fragment highlighting for the
1958 common names, since the returned documents are always TaxonBases
1961 /* The QueryFactory for creating filter queries on Distributions should
1962 * The query factory used for the common names query cannot be reused
1963 * for this case, since we want to only record the text fields which are
1964 * actually used in the primary query
1966 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1968 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1971 // search for taxa or synonyms
1972 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1973 Class taxonBaseSubclass
= TaxonBase
.class;
1974 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1975 taxonBaseSubclass
= Taxon
.class;
1976 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1977 taxonBaseSubclass
= Synonym
.class;
1979 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1980 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1981 /* A) does not work!!!!
1982 if(addDistributionFilter){
1983 // in this case we need a filter which uses a join query
1984 // to get the TaxonBase documents for the DescriptionElementBase documents
1985 // which are matching the areas in question
1986 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1988 distributionStatusList,
1989 distributionFilterQueryFactory
1991 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1994 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1995 // add additional area filter for synonyms
1996 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1997 String toField
= "accTaxon.id"; // id in TaxonBase index
1999 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2001 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2002 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2007 // search by CommonTaxonName
2008 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
2010 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2011 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
2012 "inDescription.taxon.id",
2014 QueryFactory
.addTypeRestriction(
2015 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
2016 , CommonTaxonName
.class
2018 CommonTaxonName
.class);
2019 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
2020 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2021 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
2022 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
2023 byCommonNameSearch
.setSortFields(sortFields
);
2024 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2026 luceneSearches
.add(byCommonNameSearch
);
2028 /* A) does not work!!!!
2030 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
2031 queryString, classification, null, languages, highlightFragments)
2033 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2034 if(addDistributionFilter){
2035 // in this case we are able to use DescriptionElementBase documents
2036 // which are matching the areas in question directly
2037 BooleanQuery byDistributionQuery = createByDistributionQuery(
2039 distributionStatusList,
2040 distributionFilterQueryFactory
2042 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2046 // search by misapplied names
2047 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
2049 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2050 // which allows doing query time joins
2051 // finds the misapplied name (Taxon B) which is an misapplication for
2052 // a related Taxon A.
2054 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
2055 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
2056 queryString
, classification
, languages
, highlightFragments
, sortFields
));
2057 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2059 if(addDistributionFilter
){
2060 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2063 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2064 * Maybe this is a but in java itself java.
2066 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2069 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2071 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2072 * will execute as expected:
2074 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2075 * String toField = "relation." + misappliedNameForUuid +".to.id";
2077 * Comparing both strings by the String.equals method returns true, so both String are identical.
2079 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2080 * 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)
2081 * The bug is persistent after a reboot of the development computer.
2083 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2084 // String toField = "relation." + misappliedNameForUuid +".to.id";
2085 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2086 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2087 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2089 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2090 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2091 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2093 // debug code for bug described above
2094 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2095 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2097 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2101 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2102 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2105 if(addDistributionFilter
){
2108 // in this case we need a filter which uses a join query
2109 // to get the TaxonBase documents for the DescriptionElementBase documents
2110 // which are matching the areas in question
2112 // for toTaxa, doByCommonName
2113 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2115 distributionStatusList
,
2116 distributionFilterQueryFactory
2118 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2121 if (addDistributionFilter
){
2122 multiSearch
.setFilter(multiIndexByAreaFilter
);
2126 // --- execute search
2127 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2129 // --- initialize taxa, highlight matches ....
2130 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2133 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2134 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2136 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2137 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2141 * @param namedAreaList at least one area must be in the list
2142 * @param distributionStatusList optional
2144 * @throws IOException
2146 protected Query
createByDistributionJoinQuery(
2147 List
<NamedArea
> namedAreaList
,
2148 List
<PresenceAbsenceTerm
> distributionStatusList
,
2149 QueryFactory queryFactory
2150 ) throws IOException
{
2152 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2153 String toField
= "id"; // id in TaxonBase index
2155 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2157 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2159 return taxonAreaJoinQuery
;
2163 * @param namedAreaList
2164 * @param distributionStatusList
2165 * @param queryFactory
2168 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2169 List
<PresenceAbsenceTerm
> distributionStatusList
, QueryFactory queryFactory
) {
2170 BooleanQuery areaQuery
= new BooleanQuery();
2171 // area field from Distribution
2172 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2174 // status field from Distribution
2175 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2176 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2179 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2184 * This method has been primarily created for testing the area join query but might
2185 * also be useful in other situations
2187 * @param namedAreaList
2188 * @param distributionStatusList
2189 * @param classification
2190 * @param highlightFragments
2192 * @throws IOException
2194 protected LuceneSearch
prepareByDistributionSearch(
2195 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTerm
> distributionStatusList
,
2196 Classification classification
) throws IOException
{
2198 BooleanQuery finalQuery
= new BooleanQuery();
2200 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2202 // FIXME is this query factory using the wrong type?
2203 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2205 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2206 luceneSearch
.setSortFields(sortFields
);
2209 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2211 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2213 if(classification
!= null){
2214 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2217 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2218 luceneSearch
.setQuery(finalQuery
);
2220 return luceneSearch
;
2226 * @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)
2229 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2230 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2231 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2232 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2235 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2237 // --- execute search
2238 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2240 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2241 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2243 // --- initialize taxa, highlight matches ....
2244 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2245 @SuppressWarnings("rawtypes")
2246 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2247 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2249 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2250 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2256 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2257 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2258 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2260 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2261 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2263 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2265 // --- execute search
2266 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2268 // --- initialize taxa, highlight matches ....
2269 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2271 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2272 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2273 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2275 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2276 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2278 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2279 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2286 * @param queryString
2287 * @param classification
2290 * @param highlightFragments
2291 * @param directorySelectClass
2294 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2295 String queryString
, Classification classification
, List
<Feature
> features
,
2296 List
<Language
> languages
, boolean highlightFragments
) {
2298 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2299 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2301 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2303 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2304 languages
, descriptionElementQueryFactory
);
2306 luceneSearch
.setSortFields(sortFields
);
2307 luceneSearch
.setCdmTypRestriction(clazz
);
2308 luceneSearch
.setQuery(finalQuery
);
2309 if(highlightFragments
){
2310 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2313 return luceneSearch
;
2317 * @param queryString
2318 * @param classification
2321 * @param descriptionElementQueryFactory
2324 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2325 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2326 BooleanQuery finalQuery
= new BooleanQuery();
2327 BooleanQuery textQuery
= new BooleanQuery();
2328 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2332 if(languages
== null || languages
.size() == 0){
2333 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2335 nameQuery
= new BooleanQuery();
2336 BooleanQuery languageSubQuery
= new BooleanQuery();
2337 for(Language lang
: languages
){
2338 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2340 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2341 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2343 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2346 // text field from TextData
2347 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2349 // --- TermBase fields - by representation ----
2350 // state field from CategoricalData
2351 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2353 // state field from CategoricalData
2354 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2356 // area field from Distribution
2357 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2359 // status field from Distribution
2360 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2362 finalQuery
.add(textQuery
, Occur
.MUST
);
2363 // --- classification ----
2365 if(classification
!= null){
2366 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2369 // --- IdentifieableEntity fields - by uuid
2370 if(features
!= null && features
.size() > 0 ){
2371 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2374 // the description must be associated with a taxon
2375 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2377 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2382 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2383 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2384 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2386 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2387 * or {@link MultilanguageTextFieldBridge }
2388 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2389 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2390 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2392 * TODO move to utiliy class !!!!!!!!
2394 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2396 if(stringBuilder
== null){
2397 stringBuilder
= new StringBuilder();
2399 if(languages
== null || languages
.size() == 0){
2400 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2402 for(Language lang
: languages
){
2403 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2406 return stringBuilder
;
2410 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2411 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2412 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2414 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2417 UUID nameUuid
= taxon
.getName().getUuid();
2418 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2419 String epithetOfTaxon
= null;
2420 String infragenericEpithetOfTaxon
= null;
2421 String infraspecificEpithetOfTaxon
= null;
2422 if (taxonName
.isSpecies()){
2423 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2424 } else if (taxonName
.isInfraGeneric()){
2425 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2426 } else if (taxonName
.isInfraSpecific()){
2427 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2429 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2430 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2431 List
<String
> taxonNames
= new ArrayList
<String
>();
2433 for (TaxonNode node
: nodes
){
2434 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2435 // List<String> synonymsEpithet = new ArrayList<String>();
2437 if (node
.getClassification().equals(classification
)){
2438 if (!node
.isTopmostNode()){
2439 TaxonNode parent
= node
.getParent();
2440 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2441 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2442 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2443 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2444 Rank rankOfTaxon
= taxonName
.getRank();
2447 //create inferred synonyms for species, subspecies
2448 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2450 Synonym inferredEpithet
= null;
2451 Synonym inferredGenus
= null;
2452 Synonym potentialCombination
= null;
2454 List
<String
> propertyPaths
= new ArrayList
<String
>();
2455 propertyPaths
.add("synonym");
2456 propertyPaths
.add("synonym.name");
2457 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2458 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2460 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2461 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2463 List
<TaxonRelationship
> taxonRelListParent
= null;
2464 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2465 if (doWithMisappliedNames
){
2466 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2467 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2471 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2474 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2475 Synonym syn
= synonymRelationOfParent
.getSynonym();
2477 inferredEpithet
= createInferredEpithets(taxon
,
2478 zooHashMap
, taxonName
, epithetOfTaxon
,
2479 infragenericEpithetOfTaxon
,
2480 infraspecificEpithetOfTaxon
,
2481 taxonNames
, parentName
,
2485 inferredSynonyms
.add(inferredEpithet
);
2486 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2487 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2490 if (doWithMisappliedNames
){
2492 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2493 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2495 inferredEpithet
= createInferredEpithets(taxon
,
2496 zooHashMap
, taxonName
, epithetOfTaxon
,
2497 infragenericEpithetOfTaxon
,
2498 infraspecificEpithetOfTaxon
,
2499 taxonNames
, parentName
,
2502 inferredSynonyms
.add(inferredEpithet
);
2503 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2504 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2508 if (!taxonNames
.isEmpty()){
2509 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2510 ZoologicalName name
;
2511 if (!synNotInCDM
.isEmpty()){
2512 inferredSynonymsToBeRemoved
.clear();
2514 for (Synonym syn
:inferredSynonyms
){
2515 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2516 if (!synNotInCDM
.contains(name
.getNameCache())){
2517 inferredSynonymsToBeRemoved
.add(syn
);
2521 // Remove identified Synonyms from inferredSynonyms
2522 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2523 inferredSynonyms
.remove(synonym
);
2528 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2531 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2532 TaxonNameBase synName
;
2533 ZoologicalName inferredSynName
;
2535 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2536 inferredGenus
= createInferredGenus(taxon
,
2537 zooHashMap
, taxonName
, epithetOfTaxon
,
2538 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2540 inferredSynonyms
.add(inferredGenus
);
2541 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2542 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2547 if (doWithMisappliedNames
){
2549 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2550 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2551 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2553 inferredSynonyms
.add(inferredGenus
);
2554 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2555 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2560 if (!taxonNames
.isEmpty()){
2561 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2562 ZoologicalName name
;
2563 if (!synNotInCDM
.isEmpty()){
2564 inferredSynonymsToBeRemoved
.clear();
2566 for (Synonym syn
:inferredSynonyms
){
2567 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2568 if (!synNotInCDM
.contains(name
.getNameCache())){
2569 inferredSynonymsToBeRemoved
.add(syn
);
2573 // Remove identified Synonyms from inferredSynonyms
2574 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2575 inferredSynonyms
.remove(synonym
);
2580 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2582 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2583 ZoologicalName inferredSynName
;
2584 //for all synonyms of the parent...
2585 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2586 TaxonNameBase synName
;
2587 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2588 synName
= synParent
.getName();
2590 HibernateProxyHelper
.deproxy(synParent
);
2592 // Set the sourceReference
2593 sourceReference
= synParent
.getSec();
2595 // Determine the idInSource
2596 String idInSourceParent
= getIdInSource(synParent
);
2598 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2599 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2600 String synParentInfragenericName
= null;
2601 String synParentSpecificEpithet
= null;
2603 if (parentSynZooName
.isInfraGeneric()){
2604 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2606 if (parentSynZooName
.isSpecies()){
2607 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2610 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2611 synonymsGenus.put(synGenusName, idInSource);
2614 //for all synonyms of the taxon
2616 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2618 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2619 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2620 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2622 synParentInfragenericName
,
2623 synParentSpecificEpithet
, syn
, zooHashMap
);
2625 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2626 inferredSynonyms
.add(potentialCombination
);
2627 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2628 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2635 if (doWithMisappliedNames
){
2637 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2639 TaxonNameBase misappliedParentName
;
2641 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2642 misappliedParentName
= misappliedParent
.getName();
2644 HibernateProxyHelper
.deproxy(misappliedParent
);
2646 // Set the sourceReference
2647 sourceReference
= misappliedParent
.getSec();
2649 // Determine the idInSource
2650 String idInSourceParent
= getIdInSource(misappliedParent
);
2652 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2653 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2654 String synParentInfragenericName
= null;
2655 String synParentSpecificEpithet
= null;
2657 if (parentSynZooName
.isInfraGeneric()){
2658 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2660 if (parentSynZooName
.isSpecies()){
2661 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2665 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2666 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2667 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2668 potentialCombination
= createPotentialCombination(
2669 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2671 synParentInfragenericName
,
2672 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2675 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2676 inferredSynonyms
.add(potentialCombination
);
2677 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2678 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2683 if (!taxonNames
.isEmpty()){
2684 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2685 ZoologicalName name
;
2686 if (!synNotInCDM
.isEmpty()){
2687 inferredSynonymsToBeRemoved
.clear();
2688 for (Synonym syn
:inferredSynonyms
){
2690 name
= (ZoologicalName
) syn
.getName();
2691 }catch (ClassCastException e
){
2692 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2694 if (!synNotInCDM
.contains(name
.getNameCache())){
2695 inferredSynonymsToBeRemoved
.add(syn
);
2698 // Remove identified Synonyms from inferredSynonyms
2699 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2700 inferredSynonyms
.remove(synonym
);
2706 logger
.info("The synonymrelationship type is not defined.");
2707 return inferredSynonyms
;
2714 return inferredSynonyms
;
2717 private Synonym
createPotentialCombination(String idInSourceParent
,
2718 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2719 String synParentInfragenericName
, String synParentSpecificEpithet
,
2720 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2721 Synonym potentialCombination
;
2722 Reference sourceReference
;
2723 ZoologicalName inferredSynName
;
2724 HibernateProxyHelper
.deproxy(syn
);
2726 // Set sourceReference
2727 sourceReference
= syn
.getSec();
2728 if (sourceReference
== null){
2729 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2731 if (!parentSynZooName
.getTaxa().isEmpty()){
2732 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2734 sourceReference
= taxon
.getSec();
2737 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2739 String synTaxonInfraSpecificName
= null;
2741 if (parentSynZooName
.isSpecies()){
2742 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2745 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2746 synonymsEpithet.add(epithetName);
2749 //create potential combinations...
2750 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2752 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2753 if (zooSynName
.isSpecies()){
2754 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2755 if (parentSynZooName
.isInfraGeneric()){
2756 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2759 if (zooSynName
.isInfraSpecific()){
2760 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2761 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2763 if (parentSynZooName
.isInfraGeneric()){
2764 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2768 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2770 // Set the sourceReference
2771 potentialCombination
.setSec(sourceReference
);
2774 // Determine the idInSource
2775 String idInSourceSyn
= getIdInSource(syn
);
2777 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2778 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2779 inferredSynName
.addSource(originalSource
);
2780 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2781 potentialCombination
.addSource(originalSource
);
2784 return potentialCombination
;
2787 private Synonym
createInferredGenus(Taxon taxon
,
2788 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2789 String epithetOfTaxon
, String genusOfTaxon
,
2790 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2793 Synonym inferredGenus
;
2794 TaxonNameBase synName
;
2795 ZoologicalName inferredSynName
;
2796 synName
=syn
.getName();
2797 HibernateProxyHelper
.deproxy(syn
);
2799 // Determine the idInSource
2800 String idInSourceSyn
= getIdInSource(syn
);
2801 String idInSourceTaxon
= getIdInSource(taxon
);
2802 // Determine the sourceReference
2803 Reference sourceReference
= syn
.getSec();
2805 //logger.warn(sourceReference.getTitleCache());
2807 synName
= syn
.getName();
2808 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2809 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2810 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2811 synonymsEpithet.add(synSpeciesEpithetName);
2814 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2815 //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...
2818 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2819 if (zooParentName
.isInfraGeneric()){
2820 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2823 if (taxonName
.isSpecies()){
2824 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2826 if (taxonName
.isInfraSpecific()){
2827 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2828 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2832 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2834 // Set the sourceReference
2835 inferredGenus
.setSec(sourceReference
);
2837 // Add the original source
2838 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2839 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2840 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2841 inferredGenus
.addSource(originalSource
);
2843 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2844 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2845 inferredSynName
.addSource(originalSource
);
2846 originalSource
= null;
2849 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2850 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2851 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2852 inferredGenus
.addSource(originalSource
);
2854 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2855 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2856 inferredSynName
.addSource(originalSource
);
2857 originalSource
= null;
2860 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2862 return inferredGenus
;
2865 private Synonym
createInferredEpithets(Taxon taxon
,
2866 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2867 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2868 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2869 TaxonNameBase parentName
, TaxonBase syn
) {
2871 Synonym inferredEpithet
;
2872 TaxonNameBase
<?
,?
> synName
;
2873 ZoologicalName inferredSynName
;
2874 HibernateProxyHelper
.deproxy(syn
);
2876 // Determine the idInSource
2877 String idInSourceSyn
= getIdInSource(syn
);
2878 String idInSourceTaxon
= getIdInSource(taxon
);
2879 // Determine the sourceReference
2880 Reference
<?
> sourceReference
= syn
.getSec();
2882 if (sourceReference
== null){
2883 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2884 sourceReference
= taxon
.getSec();
2887 synName
= syn
.getName();
2888 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2889 String synGenusName
= zooSynName
.getGenusOrUninomial();
2890 String synInfraGenericEpithet
= null;
2891 String synSpecificEpithet
= null;
2893 if (zooSynName
.getInfraGenericEpithet() != null){
2894 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2897 if (zooSynName
.isInfraSpecific()){
2898 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2901 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2902 synonymsGenus.put(synGenusName, idInSource);
2905 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2907 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2908 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2909 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2911 inferredSynName
.setGenusOrUninomial(synGenusName
);
2913 if (parentName
.isInfraGeneric()){
2914 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2916 if (taxonName
.isSpecies()){
2917 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2918 }else if (taxonName
.isInfraSpecific()){
2919 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2920 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2923 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2925 // Set the sourceReference
2926 inferredEpithet
.setSec(sourceReference
);
2928 /* Add the original source
2929 if (idInSource != null) {
2930 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2933 Reference citation = getCitation(syn);
2934 if (citation != null) {
2935 originalSource.setCitation(citation);
2936 inferredEpithet.addSource(originalSource);
2939 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2942 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2943 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2945 inferredEpithet
.addSource(originalSource
);
2947 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2948 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2950 inferredSynName
.addSource(originalSource
);
2954 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2956 return inferredEpithet
;
2960 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2961 * Very likely only useful for createInferredSynonyms().
2966 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2967 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2968 if (taxonName
== null) {
2969 taxonName
= zooHashMap
.get(uuid
);
2975 * Returns the idInSource for a given Synonym.
2978 private String
getIdInSource(TaxonBase taxonBase
) {
2979 String idInSource
= null;
2980 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2981 if (sources
.size() == 1) {
2982 IdentifiableSource source
= sources
.iterator().next();
2983 if (source
!= null) {
2984 idInSource
= source
.getIdInSource();
2986 } else if (sources
.size() > 1) {
2989 for (IdentifiableSource source
: sources
) {
2990 idInSource
+= source
.getIdInSource();
2991 if (count
< sources
.size()) {
2996 } else if (sources
.size() == 0){
2997 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
3006 * Returns the citation for a given Synonym.
3009 private Reference
getCitation(Synonym syn
) {
3010 Reference citation
= null;
3011 Set
<IdentifiableSource
> sources
= syn
.getSources();
3012 if (sources
.size() == 1) {
3013 IdentifiableSource source
= sources
.iterator().next();
3014 if (source
!= null) {
3015 citation
= source
.getCitation();
3017 } else if (sources
.size() > 1) {
3018 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
3025 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
3026 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
3028 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
3029 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
3030 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
3032 return inferredSynonyms
;
3036 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
3038 // TODO quickly implemented, create according dao !!!!
3039 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
3040 Set
<Classification
> classifications
= new HashSet
<Classification
>();
3041 List
<Classification
> list
= new ArrayList
<Classification
>();
3043 if (taxonBase
== null) {
3047 taxonBase
= load(taxonBase
.getUuid());
3049 if (taxonBase
instanceof Taxon
) {
3050 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
3052 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
3053 nodes
.addAll(taxon
.getTaxonNodes());
3056 for (TaxonNode node
: nodes
) {
3057 classifications
.add(node
.getClassification());
3059 list
.addAll(classifications
);
3064 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3065 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3066 // Create new synonym using concept name
3067 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3068 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3070 // Remove concept relation from taxon
3071 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3076 // Create a new synonym for the taxon
3077 SynonymRelationship synonymRelationship
;
3078 if (synonymRelationshipType
!= null
3079 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3080 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3082 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3085 this.saveOrUpdate(toTaxon
);
3086 //TODO: configurator and classification
3087 TaxonDeletionConfigurator config
= new TaxonDeletionConfigurator();
3088 config
.setDeleteNameIfPossible(false);
3089 this.deleteTaxon(fromTaxon
, config
, null);
3090 return synonymRelationship
.getSynonym();
3094 public DeleteResult
isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3095 DeleteResult result
= new DeleteResult();
3096 Set
<CdmBase
> references
= commonService
.getReferencingObjectsForDeletion(taxonBase
);
3097 if (taxonBase
instanceof Taxon
){
3098 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3099 result
= isDeletableForTaxon(references
, taxonConfig
);
3101 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3102 result
= isDeletableForSynonym(references
, synonymConfig
);
3107 private DeleteResult
isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3109 DeleteResult result
= new DeleteResult();
3110 for (CdmBase ref
: references
){
3111 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3112 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3113 result
.addException(new ReferencedObjectUndeletableException(message
));
3114 result
.addRelatedObject(ref
);
3121 private DeleteResult
isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3122 String message
= null;
3123 DeleteResult result
= new DeleteResult();
3124 for (CdmBase ref
: references
){
3125 if (!(ref
instanceof TaxonNameBase
)){
3126 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3127 message
= "The Taxon can't be deleted as long as it has synonyms.";
3130 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3131 message
= "The Taxon can't be deleted as long as it has factual data.";
3135 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3136 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3139 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3140 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3141 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3144 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3148 if (ref
instanceof PolytomousKeyNode
){
3149 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3153 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3154 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3160 /* //PolytomousKeyNode
3161 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3162 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3167 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3168 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3173 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3174 message
= "Taxon can't be deleted as it is used in a determination event";
3179 if (message
!= null){
3180 result
.addException(new ReferencedObjectUndeletableException(message
));
3181 result
.addRelatedObject(ref
);
3190 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3191 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3193 //preliminary implementation
3195 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3196 TaxonBase taxonBase
= find(taxonUuid
);
3197 if (taxonBase
== null){
3198 return new IncludedTaxaDTO();
3199 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3200 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3202 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3203 //TODO partial synonyms ??
3204 //TODO synonyms in general
3205 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3206 taxa
.addAll(syn
.getAcceptedTaxa());
3208 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3211 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3213 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3214 related
= makeRelatedIncluded(related
, result
, config
);
3221 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3223 * @return the set of conceptually related taxa for further use
3226 * @param uncheckedTaxa
3227 * @param existingTaxa
3231 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3234 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3235 for (Taxon taxon
: uncheckedTaxa
){
3236 taxonNodes
.addAll(taxon
.getTaxonNodes());
3239 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3240 if (! config
.onlyCongruent
){
3241 for (TaxonNode node
: taxonNodes
){
3242 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3243 for (TaxonNode child
: childNodes
){
3244 children
.add(child
.getTaxon());
3247 children
.remove(null); // just to be on the save side
3250 Iterator
<Taxon
> it
= children
.iterator();
3251 while(it
.hasNext()){
3252 UUID uuid
= it
.next().getUuid();
3253 if (existingTaxa
.contains(uuid
)){
3256 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3261 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3262 uncheckedAndChildren
.addAll(children
);
3264 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3267 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3272 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3273 * @return the set of these computed taxa
3275 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3276 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3278 for (Taxon taxon
: unchecked
){
3279 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3280 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3282 for (TaxonRelationship fromRel
: fromRelations
){
3283 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3286 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3287 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3288 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3290 result
.add(fromRel
.getToTaxon());
3294 for (TaxonRelationship toRel
: toRelations
){
3295 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3298 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3299 result
.add(toRel
.getFromTaxon());
3304 Iterator
<Taxon
> it
= result
.iterator();
3305 while(it
.hasNext()){
3306 UUID uuid
= it
.next().getUuid();
3307 if (existingTaxa
.contains(uuid
)){
3310 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3317 public List
<TaxonBase
> findTaxaByName(MatchingTaxonConfigurator config
){
3318 List
<TaxonBase
> taxonList
= dao
.getTaxaByName(true, false, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, 0, config
.getPropertyPath());
3323 @Transactional(readOnly
= true)
3324 public <S
extends TaxonBase
> Pager
<FindByIdentifierDTO
<S
>> findByIdentifier(
3325 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
, TaxonNode subtreeFilter
,
3326 MatchMode matchmode
, boolean includeEntity
, Integer pageSize
,
3327 Integer pageNumber
, List
<String
> propertyPaths
) {
3328 if (subtreeFilter
== null){
3329 return findByIdentifier(clazz
, identifier
, identifierType
, matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3332 Integer numberOfResults
= dao
.countByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
, matchmode
);
3333 List
<Object
[]> daoResults
= new ArrayList
<Object
[]>();
3334 if(numberOfResults
> 0) { // no point checking again
3335 daoResults
= dao
.findByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
,
3336 matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3339 List
<FindByIdentifierDTO
<S
>> result
= new ArrayList
<FindByIdentifierDTO
<S
>>();
3340 for (Object
[] daoObj
: daoResults
){
3342 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (S
)daoObj
[2]));
3344 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
3347 return new DefaultPagerImpl
<FindByIdentifierDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);