3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
11 package eu
.etaxonomy
.cdm
.api
.service
;
13 import java
.io
.IOException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.EnumSet
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
22 import java
.util
.UUID
;
24 import javax
.persistence
.EntityNotFoundException
;
26 import org
.apache
.log4j
.Logger
;
27 import org
.apache
.lucene
.index
.CorruptIndexException
;
28 import org
.apache
.lucene
.queryParser
.ParseException
;
29 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
30 import org
.apache
.lucene
.search
.BooleanFilter
;
31 import org
.apache
.lucene
.search
.BooleanQuery
;
32 import org
.apache
.lucene
.search
.DocIdSet
;
33 import org
.apache
.lucene
.search
.Query
;
34 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
35 import org
.apache
.lucene
.search
.SortField
;
36 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
37 import org
.springframework
.stereotype
.Service
;
38 import org
.springframework
.transaction
.annotation
.Transactional
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
41 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
42 import eu
.etaxonomy
.cdm
.api
.service
.config
.IncludedTaxonConfiguration
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.SynonymDeletionConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
47 import eu
.etaxonomy
.cdm
.api
.service
.dto
.FindByIdentifierDTO
;
48 import eu
.etaxonomy
.cdm
.api
.service
.dto
.IncludedTaxaDTO
;
49 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
50 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
51 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
52 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
53 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
61 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
62 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
63 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
64 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
65 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
66 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
67 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
68 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
69 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
70 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
71 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
72 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
73 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
74 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
75 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
76 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
77 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
78 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
79 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
80 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
81 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
82 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
83 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
84 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
85 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
86 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
87 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
88 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
89 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
90 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
91 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
92 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
93 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
94 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
95 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
96 import eu
.etaxonomy
.cdm
.model
.molecular
.AmplificationResult
;
97 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
98 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
99 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
100 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
101 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
102 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
103 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
104 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
105 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
106 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
107 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
108 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
115 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
116 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
117 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
118 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
119 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
122 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
123 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
124 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
125 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
126 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
127 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
128 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
129 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
130 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
134 * @author a.kohlbecker
139 @Transactional(readOnly
= true)
140 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
141 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
143 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
145 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
147 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
151 private ITaxonNameDao nameDao
;
154 private INameService nameService
;
157 private ITaxonNodeService nodeService
;
160 private ICdmGenericDao genericDao
;
163 private IDescriptionService descriptionService
;
166 private IOrderedTermVocabularyDao orderedVocabularyDao
;
169 private IOccurrenceDao occurrenceDao
;
172 private IClassificationDao classificationDao
;
175 private AbstractBeanInitializer beanInitializer
;
178 private ILuceneIndexToolProvider luceneIndexToolProvider
;
183 public TaxonServiceImpl(){
184 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
188 * FIXME Candidate for harmonization
189 * rename searchByName ?
192 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
193 return dao
.getTaxaByName(name
, sec
);
197 * FIXME Candidate for harmonization
198 * merge with getRootTaxa(Reference sec, ..., ...)
200 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
203 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
204 if (cdmFetch
== null){
205 cdmFetch
= CdmFetch
.NO_FETCH();
207 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
211 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
212 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
216 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
217 return dao
.getAllRelationships(limit
, start
);
221 * FIXME Candidate for harmonization
222 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
226 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
228 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
229 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
230 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
231 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
232 return taxonRelTypeVocabulary
;
239 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
242 @Transactional(readOnly
= false)
243 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
245 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
246 synonymName
.removeTaxonBase(synonym
);
247 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
248 taxonName
.removeTaxonBase(acceptedTaxon
);
250 synonym
.setName(taxonName
);
251 acceptedTaxon
.setName(synonymName
);
253 // the accepted taxon needs a new uuid because the concept has changed
254 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
255 //acceptedTaxon.setUuid(UUID.randomUUID());
260 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
264 @Transactional(readOnly
= false)
265 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
267 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
268 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
269 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
271 //check synonym is not homotypic
272 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
273 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
274 throw new HomotypicalGroupChangeException(message
);
277 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
279 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
280 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
281 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
283 for (Synonym heteroSynonym
: heteroSynonyms
){
284 if (synonym
.equals(heteroSynonym
)){
285 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
288 //move synonyms in same homotypic group to new accepted taxon
289 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
293 //synonym.getName().removeTaxonBase(synonym);
296 // deleteSynonym(synonym, taxon, false);
299 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
300 config
.setDeleteNameIfPossible(false);
301 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
303 } catch (Exception e
) {
304 logger
.info("Can't delete old synonym from database");
308 return newAcceptedTaxon
;
313 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
315 // Get name from synonym
316 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
318 /* // remove synonym from taxon
319 toTaxon.removeSynonym(synonym);
321 // Create a taxon with synonym name
322 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
324 // Add taxon relation
325 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
327 // since we are swapping names, we have to detach the name from the synonym completely.
328 // Otherwise the synonym will still be in the list of typified names.
329 // synonym.getName().removeTaxonBase(synonym);
330 this.deleteSynonym(synonym
, null);
337 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeHomotypicalGroupOfSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.name.HomotypicalGroup, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
339 @Transactional(readOnly
= false)
341 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
342 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
344 TaxonNameBase synonymName
= synonym
.getName();
345 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
349 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
350 newHomotypicalGroup
.addTypifiedName(synonymName
);
352 //remove existing basionym relationships
353 synonymName
.removeBasionyms();
355 //add basionym relationship
356 if (setBasionymRelationIfApplicable
){
357 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
358 for (TaxonNameBase basionym
: basionyms
){
359 synonymName
.addBasionym(basionym
);
363 //set synonym relationship correctly
364 // SynonymRelationship relToTaxon = null;
365 boolean relToTargetTaxonExists
= false;
366 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
367 for (SynonymRelationship rel
: existingRelations
){
368 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
369 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
370 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
371 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
372 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
373 rel
.setType(newRelationType
);
374 //TODO handle citation and microCitation
377 relToTargetTaxonExists
= true;
379 if (removeFromOtherTaxa
){
380 acceptedTaxon
.removeSynonym(synonym
, false);
386 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
387 Taxon acceptedTaxon
= targetTaxon
;
388 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
389 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
390 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
391 //TODO handle citation and microCitation
392 Reference citation
= null;
393 String microCitation
= null;
394 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
401 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
404 @Transactional(readOnly
= false)
405 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
407 clazz
= TaxonBase
.class;
409 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
414 protected void setDao(ITaxonDao dao
) {
419 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
420 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
422 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
423 if(numberOfResults
> 0) { // no point checking again
424 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
427 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
431 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
432 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
434 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
435 if(numberOfResults
> 0) { // no point checking again
436 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
443 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
444 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
446 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
447 if(numberOfResults
> 0) { // no point checking again
448 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
454 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
455 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
457 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
458 if(numberOfResults
> 0) { // no point checking again
459 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
461 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
465 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
466 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
468 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
469 if(numberOfResults
> 0) { // no point checking again
470 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
476 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
477 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
479 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
480 if(numberOfResults
> 0) { // no point checking again
481 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
483 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
487 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
488 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
489 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
493 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
494 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
496 List
<Taxon
> list
= new ArrayList
<Taxon
>();
499 Synonym synonym
= null;
502 synonym
= (Synonym
) dao
.load(synonymUuid
);
503 } catch (ClassCastException e
){
504 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
505 } catch (NullPointerException e
){
506 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
509 Classification classificationFilter
= null;
510 if(classificationUuid
!= null){
512 classificationFilter
= classificationDao
.load(classificationUuid
);
513 } catch (NullPointerException e
){
514 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
516 if(classificationFilter
== null){
521 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
522 if(count
> (pageSize
* pageNumber
)){
523 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
526 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
531 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
532 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
534 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
535 relatedTaxa
.remove(taxon
);
536 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
542 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
543 * <code>taxon</code> supplied as parameter.
546 * @param includeRelationships
548 * @param maxDepth can be <code>null</code> for infinite depth
551 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
557 if(includeRelationships
.isEmpty()){
561 if(maxDepth
!= null) {
564 if(logger
.isDebugEnabled()){
565 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
567 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
568 for (TaxonRelationship taxRel
: taxonRelationships
) {
571 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
574 // filter by includeRelationships
575 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
576 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
577 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
578 if(logger
.isDebugEnabled()){
579 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
581 taxa
.add(taxRel
.getToTaxon());
582 if(maxDepth
== null || maxDepth
> 0) {
583 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
586 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
587 taxa
.add(taxRel
.getFromTaxon());
588 if(logger
.isDebugEnabled()){
589 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
591 if(maxDepth
== null || maxDepth
> 0) {
592 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
602 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
605 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
606 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
608 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
609 if(numberOfResults
> 0) { // no point checking again
610 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
613 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
617 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
620 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
621 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
623 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
624 if(numberOfResults
> 0) { // no point checking again
625 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
628 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
632 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
635 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
636 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
637 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
640 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
643 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
644 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
645 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
653 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
656 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
657 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
658 return t
.getHomotypicSynonymsByHomotypicGroup();
662 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
665 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
666 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
667 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
668 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
669 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
670 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
672 return heterotypicSynonymyGroups
;
676 public List
<UuidAndTitleCache
<IdentifiableEntity
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
678 List
<UuidAndTitleCache
<IdentifiableEntity
>> results
= new ArrayList
<UuidAndTitleCache
<IdentifiableEntity
>>();
681 if (configurator
.isDoSynonyms() || configurator
.isDoTaxa() || configurator
.isDoNamesWithoutTaxa()){
682 results
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.isDoNamesWithoutTaxa(), configurator
.isDoMisappliedNames(),configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
684 if (configurator
.isDoTaxaByCommonNames()) {
686 if(configurator
.getPageSize() == null ){
687 List
<UuidAndTitleCache
<IdentifiableEntity
>> commonNameResults
= dao
.getTaxaByCommonNameForEditor(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
688 if(commonNameResults
!= null){
689 results
.addAll(commonNameResults
);
697 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
700 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
702 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
703 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
704 List
<TaxonBase
> taxa
= null;
707 long numberTaxaResults
= 0L;
710 List
<String
> propertyPath
= new ArrayList
<String
>();
711 if(configurator
.getTaxonPropertyPath() != null){
712 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
716 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
717 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
719 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
720 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
721 configurator
.getNamedAreas());
724 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
725 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
726 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
727 configurator
.getMatchMode(), configurator
.getNamedAreas(),
728 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
732 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
735 results
.addAll(taxa
);
738 numberOfResults
+= numberTaxaResults
;
740 // Names without taxa
741 if (configurator
.isDoNamesWithoutTaxa()) {
742 int numberNameResults
= 0;
744 List
<?
extends TaxonNameBase
<?
,?
>> names
=
745 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
746 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
747 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
748 if (names
.size() > 0) {
749 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
750 if (taxonName
.getTaxonBases().size() == 0) {
751 results
.add(taxonName
);
755 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
756 numberOfResults
+= numberNameResults
;
760 // Taxa from common names
762 if (configurator
.isDoTaxaByCommonNames()) {
763 taxa
= new ArrayList
<TaxonBase
>();
764 numberTaxaResults
= 0;
765 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
766 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
768 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
769 List
<Taxon
> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
770 taxa
.addAll(commonNameResults
);
773 results
.addAll(taxa
);
775 numberOfResults
+= numberTaxaResults
;
779 return new DefaultPagerImpl
<IdentifiableEntity
>
780 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
783 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
784 return dao
.getUuidAndTitleCache();
788 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
791 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
792 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
793 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
794 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
795 for (TaxonDescription taxDesc
: descriptions
){
796 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
797 for (DescriptionElementBase descElem
: elements
){
798 for(Media media
: descElem
.getMedia()){
800 //find the best matching representation
801 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
810 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
813 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
814 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
819 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
822 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
823 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
824 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
826 logger
.trace("listMedia() - START");
828 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
829 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
830 List
<Media
> nonImageGalleryImages
= new ArrayList
<Media
>();
832 if (limitToGalleries
== null) {
833 limitToGalleries
= false;
836 // --- resolve related taxa
837 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
838 logger
.trace("listMedia() - resolve related taxa");
839 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
842 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
844 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
845 logger
.trace("listMedia() - includeTaxonDescriptions");
846 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
847 // --- TaxonDescriptions
848 for (Taxon t
: taxa
) {
849 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
851 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
852 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
853 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
854 for (Media media
: element
.getMedia()) {
855 if(taxonDescription
.isImageGallery()){
856 taxonMedia
.add(media
);
859 nonImageGalleryImages
.add(media
);
865 //put images from image gallery first (#3242)
866 taxonMedia
.addAll(nonImageGalleryImages
);
870 if(includeOccurrences
!= null && includeOccurrences
) {
871 logger
.trace("listMedia() - includeOccurrences");
872 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
874 for (Taxon t
: taxa
) {
875 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
877 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
879 // direct media removed from specimen #3597
880 // taxonMedia.addAll(occurrence.getMedia());
882 // SpecimenDescriptions
883 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
884 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
885 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
886 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
887 for (DescriptionElementBase element
: elements
) {
888 for (Media media
: element
.getMedia()) {
889 taxonMedia
.add(media
);
896 //TODO why may collections have media attached? #
897 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
898 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
899 if (derivedUnit
.getCollection() != null){
900 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
904 // pherograms & gelPhotos
905 if (occurrence
.isInstanceOf(DnaSample
.class)) {
906 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
907 Set
<Sequence
> sequences
= dnaSample
.getSequences();
908 //we do show only those gelPhotos which lead to a consensus sequence
909 for (Sequence sequence
: sequences
) {
910 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
911 for (SingleRead singleRead
: sequence
.getSingleReads()){
912 AmplificationResult amplification
= singleRead
.getAmplificationResult();
913 dnaRelatedMedia
.add(amplification
.getGelPhoto());
914 dnaRelatedMedia
.add(singleRead
.getPherogram());
915 dnaRelatedMedia
.remove(null);
917 taxonMedia
.addAll(dnaRelatedMedia
);
924 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
925 logger
.trace("listMedia() - includeTaxonNameDescriptions");
926 // --- TaxonNameDescription
927 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
928 for (Taxon t
: taxa
) {
929 nameDescriptions
.addAll(t
.getName().getDescriptions());
931 for(TaxonNameDescription nameDescription
: nameDescriptions
){
932 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
933 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
934 for (DescriptionElementBase element
: elements
) {
935 for (Media media
: element
.getMedia()) {
936 taxonMedia
.add(media
);
944 logger
.trace("listMedia() - initialize");
945 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
947 logger
.trace("listMedia() - END");
953 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
956 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
957 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
961 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
964 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
965 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
969 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
972 public int countAllRelationships() {
973 return this.dao
.countAllRelationships();
980 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
983 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
984 return this.dao
.findIdenticalTaxonNames(propertyPath
);
989 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
992 public DeleteResult
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
995 config
= new TaxonDeletionConfigurator();
998 DeleteResult result
= isDeletable(taxon
, config
);
1001 // --- DeleteSynonymRelations
1002 if (config
.isDeleteSynonymRelations()){
1003 boolean removeSynonymNameFromHomotypicalGroup
= false;
1004 // use tmp Set to avoid concurrent modification
1005 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1006 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1007 for (SynonymRelationship synRel
: synRelsToDelete
){
1008 Synonym synonym
= synRel
.getSynonym();
1009 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1010 // this will cause hibernate to delete the relationship since
1011 // the SynonymRelationship field on both is annotated with removeOrphan
1012 // so no further explicit deleting of the relationship should be done here
1013 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1015 // --- DeleteSynonymsIfPossible
1016 if (config
.isDeleteSynonymsIfPossible()){
1018 boolean newHomotypicGroupIfNeeded
= true;
1019 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1020 deleteSynonym(synonym
, taxon
, synConfig
);
1022 // relationship will be deleted by hibernate automatically,
1023 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1025 // deleteSynonymRelationships(synonym, taxon);
1030 // --- DeleteTaxonRelationships
1031 if (! config
.isDeleteTaxonRelationships()){
1032 if (taxon
.getTaxonRelations().size() > 0){
1033 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1034 "Remove taxon from all relations to other taxa prior to deletion.";
1035 // throw new ReferencedObjectUndeletableException(message);
1038 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1039 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1040 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1041 if (taxon
.equals(taxRel
.getToTaxon())){
1042 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1046 taxon
.removeTaxonRelation(taxRel
);
1047 /*if (taxFrom.equals(taxon)){
1049 this.deleteTaxon(taxTo, taxConf, classification);
1050 } catch(DataChangeNoRollbackException e){
1051 logger.debug("A related taxon will not be deleted." + e.getMessage());
1055 this.deleteTaxon(taxFrom, taxConf, classification);
1056 } catch(DataChangeNoRollbackException e){
1057 logger.debug("A related taxon will not be deleted." + e.getMessage());
1065 if (config
.isDeleteDescriptions()){
1066 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1067 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1068 for (TaxonDescription desc
: descriptions
){
1069 //TODO use description delete configurator ?
1070 //FIXME check if description is ALWAYS deletable
1071 if (desc
.getDescribedSpecimenOrObservation() != null){
1072 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1073 " which also describes specimens or abservations";
1074 //throw new ReferencedObjectUndeletableException(message);
1077 removeDescriptions
.add(desc
);
1081 for (TaxonDescription desc
: removeDescriptions
){
1082 taxon
.removeDescription(desc
);
1083 descriptionService
.delete(desc
);
1088 /* //check references with only reverse mapping
1089 String message = checkForReferences(taxon);
1090 if (message != null){
1091 //throw new ReferencedObjectUndeletableException(message.toString());
1094 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1095 //if (taxon.getTaxonNodes().size() > 0){
1096 // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";
1097 // throw new ReferencedObjectUndeletableException(message);
1100 if (taxon
.getTaxonNodes().size() != 0){
1101 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1102 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1103 TaxonNode node
= null;
1104 boolean deleteChildren
;
1105 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1106 deleteChildren
= true;
1108 deleteChildren
= false;
1110 boolean success
= true;
1111 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1112 while (iterator
.hasNext()){
1113 node
= iterator
.next();
1114 if (node
.getClassification().equals(classification
)){
1120 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1121 nodeService
.delete(node
);
1124 result
.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
1126 } else if (config
.isDeleteInAllClassifications()){
1127 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1128 nodesList
.addAll(taxon
.getTaxonNodes());
1130 for (ITaxonTreeNode treeNode
: nodesList
){
1131 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1132 if(!deleteChildren
){
1133 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1134 //nodesList.addAll(taxonNode.getChildNodes());
1135 for (Object childNode: childNodes){
1136 TaxonNode childNodeCast = (TaxonNode) childNode;
1137 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1141 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1142 deleteTaxon(childNode.getTaxon(), config, classification);
1145 // taxon.removeTaxonNode(taxonNode);
1146 //nodeService.delete(taxonNode);
1149 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1150 for (Object childNode
: childNodes
){
1151 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1152 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1155 //taxon.removeTaxonNode(taxonNode);
1158 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1159 DeleteResult resultNodes
= nodeService
.deleteTaxonNodes(nodesList
, config
);
1160 if (!resultNodes
.isOk()){
1161 result
.addExceptions(resultNodes
.getExceptions());
1162 result
.setStatus(resultNodes
.getStatus());
1167 result
.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
1173 //PolytomousKey TODO
1177 if (config
.isDeleteNameIfPossible()){
1180 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1181 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1182 //check whether taxon will be deleted or not
1183 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1184 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1185 //name.removeTaxonBase(taxon);
1186 //nameService.saveOrUpdate(name);
1187 taxon
.setName(null);
1188 //dao.delete(taxon);
1189 DeleteResult nameResult
= new DeleteResult();
1191 //remove name if possible (and required)
1192 if (name
!= null && config
.isDeleteNameIfPossible()){
1194 nameResult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1200 if (nameResult
.isError()){
1201 //result.setError();
1202 result
.addRelatedObject(name
);
1203 result
.addExceptions(nameResult
.getExceptions());
1211 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1213 for (TaxonDescription desc: descriptions){
1214 if (config.isDeleteDescriptions()){
1215 //TODO use description delete configurator ?
1216 //FIXME check if description is ALWAYS deletable
1217 taxon.removeDescription(desc);
1218 descriptionService.delete(desc);
1220 if (desc.getDescribedSpecimenOrObservations().size()>0){
1221 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1222 " which also describes specimens or observations";
1223 throw new ReferencedObjectUndeletableException(message);
1228 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1230 UUID uuid
= dao
.delete(taxon
);
1232 }catch(Exception e
){
1233 result
.addException(e
);
1239 result
.addException(new Exception("The Taxon can't be deleted."));
1244 // List<Exception> exceptions = new ArrayList<Exception>();
1245 // for (String message: referencedObjects){
1246 // ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message);
1247 // exceptions.add(exception);
1249 // result.addExceptions(exceptions);
1250 // result.setError();
1257 private String
checkForReferences(Taxon taxon
){
1258 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1259 for (CdmBase referencingObject
: referencingObjects
){
1260 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1261 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1262 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";
1268 /* //PolytomousKeyNode
1269 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1270 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1275 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1276 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1281 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1282 String message
= "Taxon can't be deleted as it is used in a determination event";
1288 referencingObjects
= null;
1292 private boolean checkForPolytomousKeys(Taxon taxon
){
1293 boolean result
= false;
1294 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1295 if (!list
.isEmpty()) {
1301 @Transactional(readOnly
= false)
1302 public UUID
delete(Synonym syn
){
1303 UUID result
= syn
.getUuid();
1304 this.deleteSynonym(syn
, null);
1311 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1313 @Transactional(readOnly
= false)
1315 public DeleteResult
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1316 return deleteSynonym(synonym
, null, config
);
1322 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1324 @Transactional(readOnly
= false)
1326 public DeleteResult
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1327 DeleteResult result
= new DeleteResult();
1328 if (synonym
== null){
1333 if (config
== null){
1334 config
= new SynonymDeletionConfigurator();
1336 result
= isDeletable(synonym
, config
);
1340 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1342 //remove synonymRelationship
1343 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1345 taxonSet
.add(taxon
);
1347 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1349 for (Taxon relatedTaxon
: taxonSet
){
1350 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1351 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1353 this.saveOrUpdate(synonym
);
1355 //TODO remove name from homotypical group?
1357 //remove synonym (if necessary)
1360 if (synonym
.getSynonymRelations().isEmpty()){
1361 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1362 synonym
.setName(null);
1363 uuid
= dao
.delete(synonym
);
1365 //remove name if possible (and required)
1366 if (name
!= null && config
.isDeleteNameIfPossible()){
1368 nameService
.delete(name
, config
.getNameDeletionConfig());
1374 result
.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
1382 // List<Exception> exceptions = new ArrayList<Exception>();
1383 // for (String message :messages){
1384 // exceptions.add(new ReferencedObjectUndeletableException(message));
1386 // result.setError();
1387 // result.addExceptions(exceptions);
1394 @Transactional(readOnly
= false)
1396 public DeleteResult
deleteSynonym(UUID synonymUuid
, UUID taxonUuid
, SynonymDeletionConfigurator config
) {
1397 Synonym synonym
= HibernateProxyHelper
.deproxy(dao
.load(synonymUuid
), Synonym
.class);
1398 Taxon taxon
= HibernateProxyHelper
.deproxy(dao
.load(taxonUuid
), Taxon
.class);
1399 return deleteSynonym(synonym
, taxon
, config
);
1403 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1406 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1408 return this.dao
.findIdenticalNamesNew(propertyPath
);
1412 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1415 public String
getPhylumName(TaxonNameBase name
){
1416 return this.dao
.getPhylumName(name
);
1420 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1423 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1424 return dao
.deleteSynonymRelationships(syn
, taxon
);
1428 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1431 public long deleteSynonymRelationships(Synonym syn
) {
1432 return dao
.deleteSynonymRelationships(syn
, null);
1437 * @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)
1440 public List
<SynonymRelationship
> listSynonymRelationships(
1441 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1442 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1443 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1445 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1446 if(numberOfResults
> 0) { // no point checking again
1447 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1453 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1456 public Taxon
findBestMatchingTaxon(String taxonName
) {
1457 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1458 config
.setTaxonNameTitle(taxonName
);
1459 return findBestMatchingTaxon(config
);
1465 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1467 Taxon bestCandidate
= null;
1469 // 1. search for acceptet taxa
1470 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1471 boolean bestCandidateMatchesSecUuid
= false;
1472 boolean bestCandidateIsInClassification
= false;
1473 int countEqualCandidates
= 0;
1474 for(TaxonBase taxonBaseCandidate
: taxonList
){
1475 if(taxonBaseCandidate
instanceof Taxon
){
1476 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1477 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1478 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1480 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1481 bestCandidate
= newCanditate
;
1482 countEqualCandidates
= 1;
1483 bestCandidateMatchesSecUuid
= true;
1487 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1488 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1490 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1491 bestCandidate
= newCanditate
;
1492 countEqualCandidates
= 1;
1493 bestCandidateIsInClassification
= true;
1496 if (bestCandidate
== null){
1497 bestCandidate
= newCanditate
;
1498 countEqualCandidates
= 1;
1502 }else{ //not Taxon.class
1505 countEqualCandidates
++;
1508 if (bestCandidate
!= null){
1509 if(countEqualCandidates
> 1){
1510 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1511 return bestCandidate
;
1513 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1514 return bestCandidate
;
1519 // 2. search for synonyms
1520 if (config
.isIncludeSynonyms()){
1521 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1522 for(TaxonBase taxonBase
: synonymList
){
1523 if(taxonBase
instanceof Synonym
){
1524 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1525 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1526 if(!acceptetdCandidates
.isEmpty()){
1527 bestCandidate
= acceptetdCandidates
.iterator().next();
1528 if(acceptetdCandidates
.size() == 1){
1529 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1530 return bestCandidate
;
1532 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1533 return bestCandidate
;
1535 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1541 } catch (Exception e
){
1543 e
.printStackTrace();
1546 return bestCandidate
;
1549 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1550 UUID configClassificationUuid
= config
.getClassificationUuid();
1551 if (configClassificationUuid
== null){
1554 for (TaxonNode node
: taxon
.getTaxonNodes()){
1555 UUID classUuid
= node
.getClassification().getUuid();
1556 if (configClassificationUuid
.equals(classUuid
)){
1563 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1564 UUID configSecUuid
= config
.getSecUuid();
1565 if (configSecUuid
== null){
1568 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1569 return configSecUuid
.equals(taxonSecUuid
);
1573 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1576 public Synonym
findBestMatchingSynonym(String taxonName
) {
1577 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1578 if(! synonymList
.isEmpty()){
1579 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1580 if(synonymList
.size() == 1){
1581 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1584 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1593 * @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)
1596 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1597 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1599 Synonym synonym
= oldSynonymRelation
.getSynonym();
1600 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1601 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1602 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1603 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1604 //set default relationship type
1605 if (newSynonymRelationshipType
== null){
1606 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1608 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1610 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1611 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1612 boolean isSingleInGroup
= !(hgSize
> 1);
1614 if (! isSingleInGroup
){
1615 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1616 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1617 if (isHomotypicToAccepted
){
1618 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.";
1619 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1620 message
= String
.format(message
, homotypicRelatives
);
1621 throw new HomotypicalGroupChangeException(message
);
1623 if (! moveHomotypicGroup
){
1624 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.";
1625 throw new HomotypicalGroupChangeException(message
);
1628 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1630 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1632 SynonymRelationship result
= null;
1633 //move all synonyms to new taxon
1634 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1635 for (Synonym syn
: homotypicSynonyms
){
1636 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1637 for (SynonymRelationship synRelation
: synRelations
){
1638 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1639 Reference
<?
> newReference
= reference
;
1640 if (newReference
== null && keepReference
){
1641 newReference
= synRelation
.getCitation();
1643 String newRefDetail
= referenceDetail
;
1644 if (newRefDetail
== null && keepReference
){
1645 newRefDetail
= synRelation
.getCitationMicroReference();
1647 newTaxon
= HibernateProxyHelper
.deproxy(newTaxon
, Taxon
.class);
1648 fromTaxon
= HibernateProxyHelper
.deproxy(fromTaxon
, Taxon
.class);
1649 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1650 fromTaxon
.removeSynonymRelation(synRelation
, false);
1652 //change homotypic group of synonym if relType is 'homotypic'
1653 // if (newRelTypeIsHomotypic){
1654 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1657 if (synRelation
.equals(oldSynonymRelation
)){
1658 result
= newSynRelation
;
1664 saveOrUpdate(fromTaxon
);
1665 saveOrUpdate(newTaxon
);
1666 //Assert that there is a result
1667 if (result
== null){
1668 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1669 throw new IllegalStateException(message
);
1675 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1678 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1679 return dao
.getUuidAndTitleCacheTaxon();
1683 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1686 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1687 return dao
.getUuidAndTitleCacheSynonym();
1691 * @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)
1694 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1695 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1696 Classification classification
, List
<Language
> languages
,
1697 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1700 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1702 // --- execute search
1703 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1705 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1706 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1708 // --- initialize taxa, thighlight matches ....
1709 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1710 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1711 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1713 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1714 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1718 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTerm
> statusFilter
,
1719 Classification classification
,
1720 Integer pageSize
, Integer pageNumber
,
1721 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1723 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1725 // --- execute search
1726 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1728 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1729 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1731 // --- initialize taxa, thighlight matches ....
1732 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1733 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1734 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1736 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1737 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1742 * @param queryString
1743 * @param classification
1745 * @param highlightFragments
1746 * @param sortFields TODO
1747 * @param directorySelectClass
1750 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1751 boolean highlightFragments
, SortField
[] sortFields
) {
1752 BooleanQuery finalQuery
= new BooleanQuery();
1753 BooleanQuery textQuery
= new BooleanQuery();
1755 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1756 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1758 if(sortFields
== null){
1759 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1761 luceneSearch
.setSortFields(sortFields
);
1763 // ---- search criteria
1764 luceneSearch
.setCdmTypRestriction(clazz
);
1766 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1767 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1769 finalQuery
.add(textQuery
, Occur
.MUST
);
1771 if(classification
!= null){
1772 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1774 luceneSearch
.setQuery(finalQuery
);
1776 if(highlightFragments
){
1777 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1779 return luceneSearch
;
1783 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1784 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1785 * drawback of requiring to do the join an indexing time.
1786 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1788 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1790 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1791 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1793 * @param queryString
1794 * @param classification
1796 * @param highlightFragments
1797 * @param sortFields TODO
1800 * @throws IOException
1802 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1803 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1806 String queryTermField
;
1807 String toField
= "id"; // TaxonBase.uuid
1809 if(edge
.isBidirectional()){
1810 throw new RuntimeException("Bidirectional joining not supported!");
1813 fromField
= "relatedFrom.id";
1814 queryTermField
= "relatedFrom.titleCache";
1815 } else if(edge
.isInvers()) {
1816 fromField
= "relatedTo.id";
1817 queryTermField
= "relatedTo.titleCache";
1819 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1822 BooleanQuery finalQuery
= new BooleanQuery();
1824 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1825 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1827 BooleanQuery joinFromQuery
= new BooleanQuery();
1828 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1829 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1830 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1832 if(sortFields
== null){
1833 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1835 luceneSearch
.setSortFields(sortFields
);
1837 finalQuery
.add(joinQuery
, Occur
.MUST
);
1839 if(classification
!= null){
1840 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1842 luceneSearch
.setQuery(finalQuery
);
1844 if(highlightFragments
){
1845 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1847 return luceneSearch
;
1854 * @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)
1857 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1858 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1859 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTerm
> distributionStatus
, List
<Language
> languages
,
1860 boolean highlightFragments
, Integer pageSize
,
1861 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1862 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1864 // FIXME: allow taxonomic ordering
1865 // 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";
1866 // this require building a special sort column by a special classBridge
1867 if(highlightFragments
){
1868 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1869 "currently not fully supported by this method and thus " +
1870 "may not work with common names and misapplied names.");
1873 // convert sets to lists
1874 List
<NamedArea
> namedAreaList
= null;
1875 List
<PresenceAbsenceTerm
>distributionStatusList
= null;
1876 if(namedAreas
!= null){
1877 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1878 namedAreaList
.addAll(namedAreas
);
1880 if(distributionStatus
!= null){
1881 distributionStatusList
= new ArrayList
<PresenceAbsenceTerm
>(distributionStatus
.size());
1882 distributionStatusList
.addAll(distributionStatus
);
1885 // set default if parameter is null
1886 if(searchModes
== null){
1887 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1890 // set sort order and thus override any sort orders which may have been
1891 // defindes by prepare*Search methods
1892 if(orderHints
== null){
1893 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1895 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1897 for(OrderHint oh
: orderHints
){
1898 sortFields
[i
++] = oh
.toSortField();
1900 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1901 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1904 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1906 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1907 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1910 ======== filtering by distribution , HOWTO ========
1912 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1913 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1914 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1915 which will be put into a FilteredQuersy in the end ?
1918 3. how does it work in spatial?
1920 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1921 - http://www.infoq.com/articles/LuceneSpatialSupport
1922 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1923 ------------------------------------------------------------------------
1926 A) use a separate distribution filter per index sub-query/search:
1927 - byTaxonSyonym (query TaxaonBase):
1928 use a join area filter (Distribution -> TaxonBase)
1929 - byCommonName (query DescriptionElementBase): use an area filter on
1930 DescriptionElementBase !!! PROBLEM !!!
1931 This cannot work since the distributions are different entities than the
1932 common names and thus these are different lucene documents.
1933 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1934 use a join area filter (Distribution -> TaxonBase)
1936 B) use a common distribution filter for all index sub-query/searches:
1937 - use a common join area filter (Distribution -> TaxonBase)
1938 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1939 PROBLEM in this case: we are losing the fragment highlighting for the
1940 common names, since the returned documents are always TaxonBases
1943 /* The QueryFactory for creating filter queries on Distributions should
1944 * The query factory used for the common names query cannot be reused
1945 * for this case, since we want to only record the text fields which are
1946 * actually used in the primary query
1948 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1950 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1953 // search for taxa or synonyms
1954 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1955 Class taxonBaseSubclass
= TaxonBase
.class;
1956 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1957 taxonBaseSubclass
= Taxon
.class;
1958 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1959 taxonBaseSubclass
= Synonym
.class;
1961 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1962 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1963 /* A) does not work!!!!
1964 if(addDistributionFilter){
1965 // in this case we need a filter which uses a join query
1966 // to get the TaxonBase documents for the DescriptionElementBase documents
1967 // which are matching the areas in question
1968 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1970 distributionStatusList,
1971 distributionFilterQueryFactory
1973 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1976 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1977 // add additional area filter for synonyms
1978 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1979 String toField
= "accTaxon.id"; // id in TaxonBase index
1981 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1983 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1984 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1989 // search by CommonTaxonName
1990 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1992 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1993 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1994 "inDescription.taxon.id",
1996 QueryFactory
.addTypeRestriction(
1997 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1998 , CommonTaxonName
.class
2000 CommonTaxonName
.class);
2001 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
2002 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2003 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
2004 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
2005 byCommonNameSearch
.setSortFields(sortFields
);
2006 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2008 luceneSearches
.add(byCommonNameSearch
);
2010 /* A) does not work!!!!
2012 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
2013 queryString, classification, null, languages, highlightFragments)
2015 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2016 if(addDistributionFilter){
2017 // in this case we are able to use DescriptionElementBase documents
2018 // which are matching the areas in question directly
2019 BooleanQuery byDistributionQuery = createByDistributionQuery(
2021 distributionStatusList,
2022 distributionFilterQueryFactory
2024 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2028 // search by misapplied names
2029 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
2031 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2032 // which allows doing query time joins
2033 // finds the misapplied name (Taxon B) which is an misapplication for
2034 // a related Taxon A.
2036 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
2037 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
2038 queryString
, classification
, languages
, highlightFragments
, sortFields
));
2039 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2041 if(addDistributionFilter
){
2042 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2045 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2046 * Maybe this is a but in java itself java.
2048 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2051 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2053 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2054 * will execute as expected:
2056 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2057 * String toField = "relation." + misappliedNameForUuid +".to.id";
2059 * Comparing both strings by the String.equals method returns true, so both String are identical.
2061 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2062 * 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)
2063 * The bug is persistent after a reboot of the development computer.
2065 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2066 // String toField = "relation." + misappliedNameForUuid +".to.id";
2067 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2068 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2069 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2071 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2072 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2073 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2075 // debug code for bug described above
2076 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2077 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2079 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2083 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2084 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2087 if(addDistributionFilter
){
2090 // in this case we need a filter which uses a join query
2091 // to get the TaxonBase documents for the DescriptionElementBase documents
2092 // which are matching the areas in question
2094 // for toTaxa, doByCommonName
2095 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2097 distributionStatusList
,
2098 distributionFilterQueryFactory
2100 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2103 if (addDistributionFilter
){
2104 multiSearch
.setFilter(multiIndexByAreaFilter
);
2108 // --- execute search
2109 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2111 // --- initialize taxa, highlight matches ....
2112 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2115 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2116 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2118 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2119 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2123 * @param namedAreaList at least one area must be in the list
2124 * @param distributionStatusList optional
2126 * @throws IOException
2128 protected Query
createByDistributionJoinQuery(
2129 List
<NamedArea
> namedAreaList
,
2130 List
<PresenceAbsenceTerm
> distributionStatusList
,
2131 QueryFactory queryFactory
2132 ) throws IOException
{
2134 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2135 String toField
= "id"; // id in TaxonBase index
2137 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2139 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2141 return taxonAreaJoinQuery
;
2145 * @param namedAreaList
2146 * @param distributionStatusList
2147 * @param queryFactory
2150 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2151 List
<PresenceAbsenceTerm
> distributionStatusList
, QueryFactory queryFactory
) {
2152 BooleanQuery areaQuery
= new BooleanQuery();
2153 // area field from Distribution
2154 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2156 // status field from Distribution
2157 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2158 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2161 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2166 * This method has been primarily created for testing the area join query but might
2167 * also be useful in other situations
2169 * @param namedAreaList
2170 * @param distributionStatusList
2171 * @param classification
2172 * @param highlightFragments
2174 * @throws IOException
2176 protected LuceneSearch
prepareByDistributionSearch(
2177 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTerm
> distributionStatusList
,
2178 Classification classification
) throws IOException
{
2180 BooleanQuery finalQuery
= new BooleanQuery();
2182 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2184 // FIXME is this query factory using the wrong type?
2185 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2187 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2188 luceneSearch
.setSortFields(sortFields
);
2191 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2193 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2195 if(classification
!= null){
2196 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2199 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2200 luceneSearch
.setQuery(finalQuery
);
2202 return luceneSearch
;
2208 * @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)
2211 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2212 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2213 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2214 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2217 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2219 // --- execute search
2220 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2222 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2223 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2225 // --- initialize taxa, highlight matches ....
2226 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2227 @SuppressWarnings("rawtypes")
2228 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2229 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2231 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2232 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2238 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2239 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2240 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2242 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2243 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2245 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2247 // --- execute search
2248 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2250 // --- initialize taxa, highlight matches ....
2251 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2253 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2254 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2255 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2257 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2258 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2260 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2261 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2268 * @param queryString
2269 * @param classification
2272 * @param highlightFragments
2273 * @param directorySelectClass
2276 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2277 String queryString
, Classification classification
, List
<Feature
> features
,
2278 List
<Language
> languages
, boolean highlightFragments
) {
2280 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2281 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2283 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2285 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2286 languages
, descriptionElementQueryFactory
);
2288 luceneSearch
.setSortFields(sortFields
);
2289 luceneSearch
.setCdmTypRestriction(clazz
);
2290 luceneSearch
.setQuery(finalQuery
);
2291 if(highlightFragments
){
2292 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2295 return luceneSearch
;
2299 * @param queryString
2300 * @param classification
2303 * @param descriptionElementQueryFactory
2306 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2307 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2308 BooleanQuery finalQuery
= new BooleanQuery();
2309 BooleanQuery textQuery
= new BooleanQuery();
2310 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2314 if(languages
== null || languages
.size() == 0){
2315 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2317 nameQuery
= new BooleanQuery();
2318 BooleanQuery languageSubQuery
= new BooleanQuery();
2319 for(Language lang
: languages
){
2320 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2322 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2323 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2325 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2328 // text field from TextData
2329 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2331 // --- TermBase fields - by representation ----
2332 // state field from CategoricalData
2333 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2335 // state field from CategoricalData
2336 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2338 // area field from Distribution
2339 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2341 // status field from Distribution
2342 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2344 finalQuery
.add(textQuery
, Occur
.MUST
);
2345 // --- classification ----
2347 if(classification
!= null){
2348 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2351 // --- IdentifieableEntity fields - by uuid
2352 if(features
!= null && features
.size() > 0 ){
2353 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2356 // the description must be associated with a taxon
2357 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2359 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2364 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2365 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2366 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2368 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2369 * or {@link MultilanguageTextFieldBridge }
2370 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2371 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2372 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2374 * TODO move to utiliy class !!!!!!!!
2376 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2378 if(stringBuilder
== null){
2379 stringBuilder
= new StringBuilder();
2381 if(languages
== null || languages
.size() == 0){
2382 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2384 for(Language lang
: languages
){
2385 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2388 return stringBuilder
;
2392 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2393 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2394 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2396 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2399 UUID nameUuid
= taxon
.getName().getUuid();
2400 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2401 String epithetOfTaxon
= null;
2402 String infragenericEpithetOfTaxon
= null;
2403 String infraspecificEpithetOfTaxon
= null;
2404 if (taxonName
.isSpecies()){
2405 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2406 } else if (taxonName
.isInfraGeneric()){
2407 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2408 } else if (taxonName
.isInfraSpecific()){
2409 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2411 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2412 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2413 List
<String
> taxonNames
= new ArrayList
<String
>();
2415 for (TaxonNode node
: nodes
){
2416 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2417 // List<String> synonymsEpithet = new ArrayList<String>();
2419 if (node
.getClassification().equals(classification
)){
2420 if (!node
.isTopmostNode()){
2421 TaxonNode parent
= node
.getParent();
2422 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2423 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2424 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2425 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2426 Rank rankOfTaxon
= taxonName
.getRank();
2429 //create inferred synonyms for species, subspecies
2430 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2432 Synonym inferredEpithet
= null;
2433 Synonym inferredGenus
= null;
2434 Synonym potentialCombination
= null;
2436 List
<String
> propertyPaths
= new ArrayList
<String
>();
2437 propertyPaths
.add("synonym");
2438 propertyPaths
.add("synonym.name");
2439 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2440 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2442 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2443 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2445 List
<TaxonRelationship
> taxonRelListParent
= null;
2446 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2447 if (doWithMisappliedNames
){
2448 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2449 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2453 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2456 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2457 Synonym syn
= synonymRelationOfParent
.getSynonym();
2459 inferredEpithet
= createInferredEpithets(taxon
,
2460 zooHashMap
, taxonName
, epithetOfTaxon
,
2461 infragenericEpithetOfTaxon
,
2462 infraspecificEpithetOfTaxon
,
2463 taxonNames
, parentName
,
2467 inferredSynonyms
.add(inferredEpithet
);
2468 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2469 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2472 if (doWithMisappliedNames
){
2474 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2475 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2477 inferredEpithet
= createInferredEpithets(taxon
,
2478 zooHashMap
, taxonName
, epithetOfTaxon
,
2479 infragenericEpithetOfTaxon
,
2480 infraspecificEpithetOfTaxon
,
2481 taxonNames
, parentName
,
2484 inferredSynonyms
.add(inferredEpithet
);
2485 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2486 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2490 if (!taxonNames
.isEmpty()){
2491 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2492 ZoologicalName name
;
2493 if (!synNotInCDM
.isEmpty()){
2494 inferredSynonymsToBeRemoved
.clear();
2496 for (Synonym syn
:inferredSynonyms
){
2497 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2498 if (!synNotInCDM
.contains(name
.getNameCache())){
2499 inferredSynonymsToBeRemoved
.add(syn
);
2503 // Remove identified Synonyms from inferredSynonyms
2504 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2505 inferredSynonyms
.remove(synonym
);
2510 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2513 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2514 TaxonNameBase synName
;
2515 ZoologicalName inferredSynName
;
2517 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2518 inferredGenus
= createInferredGenus(taxon
,
2519 zooHashMap
, taxonName
, epithetOfTaxon
,
2520 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2522 inferredSynonyms
.add(inferredGenus
);
2523 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2524 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2529 if (doWithMisappliedNames
){
2531 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2532 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2533 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2535 inferredSynonyms
.add(inferredGenus
);
2536 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2537 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2542 if (!taxonNames
.isEmpty()){
2543 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2544 ZoologicalName name
;
2545 if (!synNotInCDM
.isEmpty()){
2546 inferredSynonymsToBeRemoved
.clear();
2548 for (Synonym syn
:inferredSynonyms
){
2549 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2550 if (!synNotInCDM
.contains(name
.getNameCache())){
2551 inferredSynonymsToBeRemoved
.add(syn
);
2555 // Remove identified Synonyms from inferredSynonyms
2556 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2557 inferredSynonyms
.remove(synonym
);
2562 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2564 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2565 ZoologicalName inferredSynName
;
2566 //for all synonyms of the parent...
2567 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2568 TaxonNameBase synName
;
2569 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2570 synName
= synParent
.getName();
2572 HibernateProxyHelper
.deproxy(synParent
);
2574 // Set the sourceReference
2575 sourceReference
= synParent
.getSec();
2577 // Determine the idInSource
2578 String idInSourceParent
= getIdInSource(synParent
);
2580 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2581 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2582 String synParentInfragenericName
= null;
2583 String synParentSpecificEpithet
= null;
2585 if (parentSynZooName
.isInfraGeneric()){
2586 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2588 if (parentSynZooName
.isSpecies()){
2589 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2592 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2593 synonymsGenus.put(synGenusName, idInSource);
2596 //for all synonyms of the taxon
2598 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2600 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2601 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2602 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2604 synParentInfragenericName
,
2605 synParentSpecificEpithet
, syn
, zooHashMap
);
2607 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2608 inferredSynonyms
.add(potentialCombination
);
2609 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2610 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2617 if (doWithMisappliedNames
){
2619 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2621 TaxonNameBase misappliedParentName
;
2623 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2624 misappliedParentName
= misappliedParent
.getName();
2626 HibernateProxyHelper
.deproxy(misappliedParent
);
2628 // Set the sourceReference
2629 sourceReference
= misappliedParent
.getSec();
2631 // Determine the idInSource
2632 String idInSourceParent
= getIdInSource(misappliedParent
);
2634 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2635 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2636 String synParentInfragenericName
= null;
2637 String synParentSpecificEpithet
= null;
2639 if (parentSynZooName
.isInfraGeneric()){
2640 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2642 if (parentSynZooName
.isSpecies()){
2643 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2647 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2648 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2649 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2650 potentialCombination
= createPotentialCombination(
2651 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2653 synParentInfragenericName
,
2654 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2657 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2658 inferredSynonyms
.add(potentialCombination
);
2659 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2660 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2665 if (!taxonNames
.isEmpty()){
2666 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2667 ZoologicalName name
;
2668 if (!synNotInCDM
.isEmpty()){
2669 inferredSynonymsToBeRemoved
.clear();
2670 for (Synonym syn
:inferredSynonyms
){
2672 name
= (ZoologicalName
) syn
.getName();
2673 }catch (ClassCastException e
){
2674 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2676 if (!synNotInCDM
.contains(name
.getNameCache())){
2677 inferredSynonymsToBeRemoved
.add(syn
);
2680 // Remove identified Synonyms from inferredSynonyms
2681 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2682 inferredSynonyms
.remove(synonym
);
2688 logger
.info("The synonymrelationship type is not defined.");
2689 return inferredSynonyms
;
2696 return inferredSynonyms
;
2699 private Synonym
createPotentialCombination(String idInSourceParent
,
2700 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2701 String synParentInfragenericName
, String synParentSpecificEpithet
,
2702 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2703 Synonym potentialCombination
;
2704 Reference sourceReference
;
2705 ZoologicalName inferredSynName
;
2706 HibernateProxyHelper
.deproxy(syn
);
2708 // Set sourceReference
2709 sourceReference
= syn
.getSec();
2710 if (sourceReference
== null){
2711 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2713 if (!parentSynZooName
.getTaxa().isEmpty()){
2714 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2716 sourceReference
= taxon
.getSec();
2719 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2721 String synTaxonInfraSpecificName
= null;
2723 if (parentSynZooName
.isSpecies()){
2724 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2727 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2728 synonymsEpithet.add(epithetName);
2731 //create potential combinations...
2732 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2734 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2735 if (zooSynName
.isSpecies()){
2736 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2737 if (parentSynZooName
.isInfraGeneric()){
2738 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2741 if (zooSynName
.isInfraSpecific()){
2742 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2743 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2745 if (parentSynZooName
.isInfraGeneric()){
2746 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2750 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2752 // Set the sourceReference
2753 potentialCombination
.setSec(sourceReference
);
2756 // Determine the idInSource
2757 String idInSourceSyn
= getIdInSource(syn
);
2759 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2760 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2761 inferredSynName
.addSource(originalSource
);
2762 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2763 potentialCombination
.addSource(originalSource
);
2766 return potentialCombination
;
2769 private Synonym
createInferredGenus(Taxon taxon
,
2770 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2771 String epithetOfTaxon
, String genusOfTaxon
,
2772 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2775 Synonym inferredGenus
;
2776 TaxonNameBase synName
;
2777 ZoologicalName inferredSynName
;
2778 synName
=syn
.getName();
2779 HibernateProxyHelper
.deproxy(syn
);
2781 // Determine the idInSource
2782 String idInSourceSyn
= getIdInSource(syn
);
2783 String idInSourceTaxon
= getIdInSource(taxon
);
2784 // Determine the sourceReference
2785 Reference sourceReference
= syn
.getSec();
2787 //logger.warn(sourceReference.getTitleCache());
2789 synName
= syn
.getName();
2790 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2791 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2792 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2793 synonymsEpithet.add(synSpeciesEpithetName);
2796 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2797 //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...
2800 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2801 if (zooParentName
.isInfraGeneric()){
2802 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2805 if (taxonName
.isSpecies()){
2806 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2808 if (taxonName
.isInfraSpecific()){
2809 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2810 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2814 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2816 // Set the sourceReference
2817 inferredGenus
.setSec(sourceReference
);
2819 // Add the original source
2820 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2821 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2822 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2823 inferredGenus
.addSource(originalSource
);
2825 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2826 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2827 inferredSynName
.addSource(originalSource
);
2828 originalSource
= null;
2831 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2832 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2833 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2834 inferredGenus
.addSource(originalSource
);
2836 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2837 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2838 inferredSynName
.addSource(originalSource
);
2839 originalSource
= null;
2842 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2844 return inferredGenus
;
2847 private Synonym
createInferredEpithets(Taxon taxon
,
2848 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2849 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2850 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2851 TaxonNameBase parentName
, TaxonBase syn
) {
2853 Synonym inferredEpithet
;
2854 TaxonNameBase
<?
,?
> synName
;
2855 ZoologicalName inferredSynName
;
2856 HibernateProxyHelper
.deproxy(syn
);
2858 // Determine the idInSource
2859 String idInSourceSyn
= getIdInSource(syn
);
2860 String idInSourceTaxon
= getIdInSource(taxon
);
2861 // Determine the sourceReference
2862 Reference
<?
> sourceReference
= syn
.getSec();
2864 if (sourceReference
== null){
2865 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2866 sourceReference
= taxon
.getSec();
2869 synName
= syn
.getName();
2870 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2871 String synGenusName
= zooSynName
.getGenusOrUninomial();
2872 String synInfraGenericEpithet
= null;
2873 String synSpecificEpithet
= null;
2875 if (zooSynName
.getInfraGenericEpithet() != null){
2876 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2879 if (zooSynName
.isInfraSpecific()){
2880 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2883 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2884 synonymsGenus.put(synGenusName, idInSource);
2887 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2889 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2890 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2891 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2893 inferredSynName
.setGenusOrUninomial(synGenusName
);
2895 if (parentName
.isInfraGeneric()){
2896 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2898 if (taxonName
.isSpecies()){
2899 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2900 }else if (taxonName
.isInfraSpecific()){
2901 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2902 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2905 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2907 // Set the sourceReference
2908 inferredEpithet
.setSec(sourceReference
);
2910 /* Add the original source
2911 if (idInSource != null) {
2912 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2915 Reference citation = getCitation(syn);
2916 if (citation != null) {
2917 originalSource.setCitation(citation);
2918 inferredEpithet.addSource(originalSource);
2921 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2924 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2925 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2927 inferredEpithet
.addSource(originalSource
);
2929 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2930 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2932 inferredSynName
.addSource(originalSource
);
2936 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2938 return inferredEpithet
;
2942 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2943 * Very likely only useful for createInferredSynonyms().
2948 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2949 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2950 if (taxonName
== null) {
2951 taxonName
= zooHashMap
.get(uuid
);
2957 * Returns the idInSource for a given Synonym.
2960 private String
getIdInSource(TaxonBase taxonBase
) {
2961 String idInSource
= null;
2962 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2963 if (sources
.size() == 1) {
2964 IdentifiableSource source
= sources
.iterator().next();
2965 if (source
!= null) {
2966 idInSource
= source
.getIdInSource();
2968 } else if (sources
.size() > 1) {
2971 for (IdentifiableSource source
: sources
) {
2972 idInSource
+= source
.getIdInSource();
2973 if (count
< sources
.size()) {
2978 } else if (sources
.size() == 0){
2979 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2988 * Returns the citation for a given Synonym.
2991 private Reference
getCitation(Synonym syn
) {
2992 Reference citation
= null;
2993 Set
<IdentifiableSource
> sources
= syn
.getSources();
2994 if (sources
.size() == 1) {
2995 IdentifiableSource source
= sources
.iterator().next();
2996 if (source
!= null) {
2997 citation
= source
.getCitation();
2999 } else if (sources
.size() > 1) {
3000 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
3007 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
3008 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
3010 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
3011 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
3012 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
3014 return inferredSynonyms
;
3018 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
3020 // TODO quickly implemented, create according dao !!!!
3021 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
3022 Set
<Classification
> classifications
= new HashSet
<Classification
>();
3023 List
<Classification
> list
= new ArrayList
<Classification
>();
3025 if (taxonBase
== null) {
3029 taxonBase
= load(taxonBase
.getUuid());
3031 if (taxonBase
instanceof Taxon
) {
3032 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
3034 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
3035 nodes
.addAll(taxon
.getTaxonNodes());
3038 for (TaxonNode node
: nodes
) {
3039 classifications
.add(node
.getClassification());
3041 list
.addAll(classifications
);
3046 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3047 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3048 // Create new synonym using concept name
3049 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3050 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3052 // Remove concept relation from taxon
3053 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3058 // Create a new synonym for the taxon
3059 SynonymRelationship synonymRelationship
;
3060 if (synonymRelationshipType
!= null
3061 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3062 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3064 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3067 this.saveOrUpdate(toTaxon
);
3068 //TODO: configurator and classification
3069 TaxonDeletionConfigurator config
= new TaxonDeletionConfigurator();
3070 config
.setDeleteNameIfPossible(false);
3071 this.deleteTaxon(fromTaxon
, config
, null);
3072 return synonymRelationship
.getSynonym();
3076 public DeleteResult
isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3077 DeleteResult result
= new DeleteResult();
3078 Set
<CdmBase
> references
= commonService
.getReferencingObjectsForDeletion(taxonBase
);
3079 if (taxonBase
instanceof Taxon
){
3080 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3081 result
= isDeletableForTaxon(references
, taxonConfig
);
3083 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3084 result
= isDeletableForSynonym(references
, synonymConfig
);
3089 private DeleteResult
isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3091 DeleteResult result
= new DeleteResult();
3092 for (CdmBase ref
: references
){
3093 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3094 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3095 result
.addException(new ReferencedObjectUndeletableException(message
));
3096 result
.addRelatedObject(ref
);
3103 private DeleteResult
isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3104 String message
= null;
3105 DeleteResult result
= new DeleteResult();
3106 for (CdmBase ref
: references
){
3107 if (!(ref
instanceof TaxonNameBase
)){
3108 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3109 message
= "The Taxon can't be deleted as long as it has synonyms.";
3112 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3113 message
= "The Taxon can't be deleted as long as it has factual data.";
3117 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3118 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3121 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3122 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3123 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3126 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3130 if (ref
instanceof PolytomousKeyNode
){
3131 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3135 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3136 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3142 /* //PolytomousKeyNode
3143 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3144 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3149 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3150 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3155 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3156 message
= "Taxon can't be deleted as it is used in a determination event";
3161 if (message
!= null){
3162 result
.addException(new ReferencedObjectUndeletableException(message
));
3163 result
.addRelatedObject(ref
);
3172 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3173 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3175 //preliminary implementation
3177 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3178 TaxonBase taxonBase
= find(taxonUuid
);
3179 if (taxonBase
== null){
3180 return new IncludedTaxaDTO();
3181 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3182 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3184 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3185 //TODO partial synonyms ??
3186 //TODO synonyms in general
3187 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3188 taxa
.addAll(syn
.getAcceptedTaxa());
3190 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3193 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3195 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3196 related
= makeRelatedIncluded(related
, result
, config
);
3203 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3205 * @return the set of conceptually related taxa for further use
3208 * @param uncheckedTaxa
3209 * @param existingTaxa
3213 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3216 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3217 for (Taxon taxon
: uncheckedTaxa
){
3218 taxonNodes
.addAll(taxon
.getTaxonNodes());
3221 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3222 if (! config
.onlyCongruent
){
3223 for (TaxonNode node
: taxonNodes
){
3224 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3225 for (TaxonNode child
: childNodes
){
3226 children
.add(child
.getTaxon());
3229 children
.remove(null); // just to be on the save side
3232 Iterator
<Taxon
> it
= children
.iterator();
3233 while(it
.hasNext()){
3234 UUID uuid
= it
.next().getUuid();
3235 if (existingTaxa
.contains(uuid
)){
3238 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3243 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3244 uncheckedAndChildren
.addAll(children
);
3246 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3249 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3254 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3255 * @return the set of these computed taxa
3257 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3258 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3260 for (Taxon taxon
: unchecked
){
3261 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3262 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3264 for (TaxonRelationship fromRel
: fromRelations
){
3265 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3268 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3269 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3270 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3272 result
.add(fromRel
.getToTaxon());
3276 for (TaxonRelationship toRel
: toRelations
){
3277 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3280 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3281 result
.add(toRel
.getFromTaxon());
3286 Iterator
<Taxon
> it
= result
.iterator();
3287 while(it
.hasNext()){
3288 UUID uuid
= it
.next().getUuid();
3289 if (existingTaxa
.contains(uuid
)){
3292 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3299 public List
<TaxonBase
> findTaxaByName(MatchingTaxonConfigurator config
){
3300 List
<TaxonBase
> taxonList
= dao
.getTaxaByName(true, false, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, 0, config
.getPropertyPath());
3305 @Transactional(readOnly
= true)
3306 public <S
extends TaxonBase
> Pager
<FindByIdentifierDTO
<S
>> findByIdentifier(
3307 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
, TaxonNode subtreeFilter
,
3308 MatchMode matchmode
, boolean includeEntity
, Integer pageSize
,
3309 Integer pageNumber
, List
<String
> propertyPaths
) {
3310 if (subtreeFilter
== null){
3311 return findByIdentifier(clazz
, identifier
, identifierType
, matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3314 Integer numberOfResults
= dao
.countByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
, matchmode
);
3315 List
<Object
[]> daoResults
= new ArrayList
<Object
[]>();
3316 if(numberOfResults
> 0) { // no point checking again
3317 daoResults
= dao
.findByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
,
3318 matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3321 List
<FindByIdentifierDTO
<S
>> result
= new ArrayList
<FindByIdentifierDTO
<S
>>();
3322 for (Object
[] daoObj
: daoResults
){
3324 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (S
)daoObj
[2]));
3326 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
3329 return new DefaultPagerImpl
<FindByIdentifierDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);