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
.IncludedTaxaDTO
;
48 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
49 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
50 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
51 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
61 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
62 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
63 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
64 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
65 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
66 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
67 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
68 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
69 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
70 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
71 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
72 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
73 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
74 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
75 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
76 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
77 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
78 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
79 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
80 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
81 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
82 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
83 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
84 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
85 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
86 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
87 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
88 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
89 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
90 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
91 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
92 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
93 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
94 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
95 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
96 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
97 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
98 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
99 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
100 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
101 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
102 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
103 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
104 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
105 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
106 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
108 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
115 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
116 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
117 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
118 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
119 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
122 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
123 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
124 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
125 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
126 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
127 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
131 * @author a.kohlbecker
136 @Transactional(readOnly
= true)
137 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
138 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
140 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
142 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
144 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
148 private ITaxonNameDao nameDao
;
151 private INameService nameService
;
154 private ITaxonNodeService nodeService
;
157 private ICdmGenericDao genericDao
;
160 private IDescriptionService descriptionService
;
163 private IOrderedTermVocabularyDao orderedVocabularyDao
;
166 private IOccurrenceDao occurrenceDao
;
169 private IClassificationDao classificationDao
;
172 private AbstractBeanInitializer beanInitializer
;
175 private ILuceneIndexToolProvider luceneIndexToolProvider
;
180 public TaxonServiceImpl(){
181 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
185 * FIXME Candidate for harmonization
186 * rename searchByName ?
189 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
190 return dao
.getTaxaByName(name
, sec
);
194 * FIXME Candidate for harmonization
195 * merge with getRootTaxa(Reference sec, ..., ...)
197 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
200 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
201 if (cdmFetch
== null){
202 cdmFetch
= CdmFetch
.NO_FETCH();
204 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
208 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
209 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
213 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
214 return dao
.getAllRelationships(limit
, start
);
218 * FIXME Candidate for harmonization
219 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
223 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
225 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
226 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
227 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
228 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
229 return taxonRelTypeVocabulary
;
236 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
239 @Transactional(readOnly
= false)
240 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
242 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
243 synonymName
.removeTaxonBase(synonym
);
244 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
245 taxonName
.removeTaxonBase(acceptedTaxon
);
247 synonym
.setName(taxonName
);
248 acceptedTaxon
.setName(synonymName
);
250 // the accepted taxon needs a new uuid because the concept has changed
251 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
252 //acceptedTaxon.setUuid(UUID.randomUUID());
257 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
261 @Transactional(readOnly
= false)
262 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
264 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
265 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
266 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
268 //check synonym is not homotypic
269 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
270 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
271 throw new HomotypicalGroupChangeException(message
);
274 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
276 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
277 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
278 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
280 for (Synonym heteroSynonym
: heteroSynonyms
){
281 if (synonym
.equals(heteroSynonym
)){
282 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
285 //move synonyms in same homotypic group to new accepted taxon
286 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
290 //synonym.getName().removeTaxonBase(synonym);
293 // deleteSynonym(synonym, taxon, false);
296 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
297 config
.setDeleteNameIfPossible(false);
298 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
300 } catch (Exception e
) {
301 logger
.info("Can't delete old synonym from database");
305 return newAcceptedTaxon
;
310 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
312 // Get name from synonym
313 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
315 /* // remove synonym from taxon
316 toTaxon.removeSynonym(synonym);
318 // Create a taxon with synonym name
319 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
321 // Add taxon relation
322 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
324 // since we are swapping names, we have to detach the name from the synonym completely.
325 // Otherwise the synonym will still be in the list of typified names.
326 // synonym.getName().removeTaxonBase(synonym);
327 this.deleteSynonym(synonym
, null);
334 * @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)
336 @Transactional(readOnly
= false)
338 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
339 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
341 TaxonNameBase synonymName
= synonym
.getName();
342 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
346 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
347 newHomotypicalGroup
.addTypifiedName(synonymName
);
349 //remove existing basionym relationships
350 synonymName
.removeBasionyms();
352 //add basionym relationship
353 if (setBasionymRelationIfApplicable
){
354 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
355 for (TaxonNameBase basionym
: basionyms
){
356 synonymName
.addBasionym(basionym
);
360 //set synonym relationship correctly
361 // SynonymRelationship relToTaxon = null;
362 boolean relToTargetTaxonExists
= false;
363 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
364 for (SynonymRelationship rel
: existingRelations
){
365 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
366 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
367 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
368 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
369 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
370 rel
.setType(newRelationType
);
371 //TODO handle citation and microCitation
374 relToTargetTaxonExists
= true;
376 if (removeFromOtherTaxa
){
377 acceptedTaxon
.removeSynonym(synonym
, false);
383 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
384 Taxon acceptedTaxon
= targetTaxon
;
385 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
386 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
387 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
388 //TODO handle citation and microCitation
389 Reference citation
= null;
390 String microCitation
= null;
391 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
398 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
401 @Transactional(readOnly
= false)
402 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
404 clazz
= TaxonBase
.class;
406 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
411 protected void setDao(ITaxonDao dao
) {
416 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
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 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
434 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
435 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
437 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
438 if(numberOfResults
> 0) { // no point checking again
439 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
446 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
449 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
450 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
452 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
453 if(numberOfResults
> 0) { // no point checking again
454 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
460 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
463 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
464 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
466 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
467 if(numberOfResults
> 0) { // no point checking again
468 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
470 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
474 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
477 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
478 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
480 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
481 if(numberOfResults
> 0) { // no point checking again
482 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
488 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
491 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
492 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
494 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
495 if(numberOfResults
> 0) { // no point checking again
496 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
498 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
502 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
503 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
504 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
508 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
509 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
511 List
<Taxon
> list
= new ArrayList
<Taxon
>();
514 Synonym synonym
= null;
517 synonym
= (Synonym
) dao
.load(synonymUuid
);
518 } catch (ClassCastException e
){
519 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
520 } catch (NullPointerException e
){
521 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
524 Classification classificationFilter
= null;
525 if(classificationUuid
!= null){
527 classificationFilter
= classificationDao
.load(classificationUuid
);
528 } catch (NullPointerException e
){
529 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
531 if(classificationFilter
== null){
536 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
537 if(count
> (pageSize
* pageNumber
)){
538 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
541 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
547 * @param includeRelationships
551 * @param propertyPaths
552 * @return an List which is not specifically ordered
555 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
556 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
558 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
559 relatedTaxa
.remove(taxon
);
560 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
566 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
567 * <code>taxon</code> supplied as parameter.
570 * @param includeRelationships
572 * @param maxDepth can be <code>null</code> for infinite depth
575 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
581 if(includeRelationships
.isEmpty()){
585 if(maxDepth
!= null) {
588 if(logger
.isDebugEnabled()){
589 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
591 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
592 for (TaxonRelationship taxRel
: taxonRelationships
) {
595 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
598 // filter by includeRelationships
599 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
600 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
601 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
602 if(logger
.isDebugEnabled()){
603 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
605 taxa
.add(taxRel
.getToTaxon());
606 if(maxDepth
== null || maxDepth
> 0) {
607 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
610 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
611 taxa
.add(taxRel
.getFromTaxon());
612 if(logger
.isDebugEnabled()){
613 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
615 if(maxDepth
== null || maxDepth
> 0) {
616 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
626 * @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)
629 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
630 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
632 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
633 if(numberOfResults
> 0) { // no point checking again
634 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
637 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
641 * @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)
644 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
645 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
647 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
648 if(numberOfResults
> 0) { // no point checking again
649 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
652 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
656 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
659 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
660 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
661 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
664 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
667 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
668 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
669 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
677 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
680 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
681 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
682 return t
.getHomotypicSynonymsByHomotypicGroup();
686 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
689 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
690 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
691 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
692 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
693 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
694 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
696 return heterotypicSynonymyGroups
;
700 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
702 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
703 // Class<? extends TaxonBase> clazz = null;
704 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
705 // clazz = TaxonBase.class;
706 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
707 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
708 // } else if(configurator.isDoTaxa()) {
709 // clazz = Taxon.class;
710 // //propertyPath = configurator.getTaxonPropertyPath();
711 // } else if (configurator.isDoSynonyms()) {
712 // clazz = Synonym.class;
713 // //propertyPath = configurator.getSynonymPropertyPath();
717 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
722 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
725 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
727 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
728 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
729 List
<TaxonBase
> taxa
= null;
732 long numberTaxaResults
= 0L;
735 List
<String
> propertyPath
= new ArrayList
<String
>();
736 if(configurator
.getTaxonPropertyPath() != null){
737 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
741 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
742 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
744 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
745 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
746 configurator
.getNamedAreas());
749 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
750 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
751 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
752 configurator
.getMatchMode(), configurator
.getNamedAreas(),
753 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
757 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
760 results
.addAll(taxa
);
763 numberOfResults
+= numberTaxaResults
;
765 // Names without taxa
766 if (configurator
.isDoNamesWithoutTaxa()) {
767 int numberNameResults
= 0;
769 List
<?
extends TaxonNameBase
<?
,?
>> names
=
770 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
771 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
772 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
773 if (names
.size() > 0) {
774 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
775 if (taxonName
.getTaxonBases().size() == 0) {
776 results
.add(taxonName
);
780 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
781 numberOfResults
+= numberNameResults
;
785 // Taxa from common names
787 if (configurator
.isDoTaxaByCommonNames()) {
788 taxa
= new ArrayList
<TaxonBase
>();
789 numberTaxaResults
= 0;
790 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
791 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
793 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
794 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
795 for( Object
[] entry
: commonNameResults
) {
796 taxa
.add((TaxonBase
) entry
[0]);
800 results
.addAll(taxa
);
802 numberOfResults
+= numberTaxaResults
;
806 return new DefaultPagerImpl
<IdentifiableEntity
>
807 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
810 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
811 return dao
.getUuidAndTitleCache();
815 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
818 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
819 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
820 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
821 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
822 for (TaxonDescription taxDesc
: descriptions
){
823 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
824 for (DescriptionElementBase descElem
: elements
){
825 for(Media media
: descElem
.getMedia()){
827 //find the best matching representation
828 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
837 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
840 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
841 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
846 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
849 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
850 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
851 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
853 logger
.trace("listMedia() - START");
855 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
856 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
858 if (limitToGalleries
== null) {
859 limitToGalleries
= false;
862 // --- resolve related taxa
863 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
864 logger
.trace("listMedia() - resolve related taxa");
865 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
868 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
870 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
871 logger
.trace("listMedia() - includeTaxonDescriptions");
872 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
873 // --- TaxonDescriptions
874 for (Taxon t
: taxa
) {
875 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
877 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
878 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
879 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
880 for (Media media
: element
.getMedia()) {
881 taxonMedia
.add(media
);
889 if(includeOccurrences
!= null && includeOccurrences
) {
890 logger
.trace("listMedia() - includeOccurrences");
891 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
893 for (Taxon t
: taxa
) {
894 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
896 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
898 // direct media removed from specimen #3597
899 // taxonMedia.addAll(occurrence.getMedia());
901 // SpecimenDescriptions
902 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
903 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
904 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
905 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
906 for (DescriptionElementBase element
: elements
) {
907 for (Media media
: element
.getMedia()) {
908 taxonMedia
.add(media
);
915 //TODO why may collections have media attached? #
916 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
917 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
918 if (derivedUnit
.getCollection() != null){
919 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
923 // pherograms & gelPhotos
924 if (occurrence
.isInstanceOf(DnaSample
.class)) {
925 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
926 Set
<Sequence
> sequences
= dnaSample
.getSequences();
927 //we do show only those gelPhotos which lead to a consensus sequence
928 for (Sequence sequence
: sequences
) {
929 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
930 for (SingleRead singleRead
: sequence
.getSingleReads()){
931 Amplification amplification
= singleRead
.getAmplification();
932 dnaRelatedMedia
.add(amplification
.getGelPhoto());
933 dnaRelatedMedia
.add(singleRead
.getPherogram());
934 dnaRelatedMedia
.remove(null);
936 taxonMedia
.addAll(dnaRelatedMedia
);
943 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
944 logger
.trace("listMedia() - includeTaxonNameDescriptions");
945 // --- TaxonNameDescription
946 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
947 for (Taxon t
: taxa
) {
948 nameDescriptions
.addAll(t
.getName().getDescriptions());
950 for(TaxonNameDescription nameDescription
: nameDescriptions
){
951 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
952 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
953 for (DescriptionElementBase element
: elements
) {
954 for (Media media
: element
.getMedia()) {
955 taxonMedia
.add(media
);
963 logger
.trace("listMedia() - initialize");
964 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
966 logger
.trace("listMedia() - END");
972 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
975 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
976 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
980 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
983 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
984 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
988 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
991 public int countAllRelationships() {
992 return this.dao
.countAllRelationships();
999 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
1002 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
1003 return this.dao
.findIdenticalTaxonNames(propertyPath
);
1008 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1011 public String
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
1012 if (config
== null){
1013 config
= new TaxonDeletionConfigurator();
1016 List
<String
> referencedObjects
= isDeletable(taxon
, config
);
1018 if (referencedObjects
.isEmpty()){
1019 // --- DeleteSynonymRelations
1020 if (config
.isDeleteSynonymRelations()){
1021 boolean removeSynonymNameFromHomotypicalGroup
= false;
1022 // use tmp Set to avoid concurrent modification
1023 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1024 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1025 for (SynonymRelationship synRel
: synRelsToDelete
){
1026 Synonym synonym
= synRel
.getSynonym();
1027 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1028 // this will cause hibernate to delete the relationship since
1029 // the SynonymRelationship field on both is annotated with removeOrphan
1030 // so no further explicit deleting of the relationship should be done here
1031 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1033 // --- DeleteSynonymsIfPossible
1034 if (config
.isDeleteSynonymsIfPossible()){
1036 boolean newHomotypicGroupIfNeeded
= true;
1037 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1038 deleteSynonym(synonym
, taxon
, synConfig
);
1040 // relationship will be deleted by hibernate automatically,
1041 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1043 // deleteSynonymRelationships(synonym, taxon);
1048 // --- DeleteTaxonRelationships
1049 if (! config
.isDeleteTaxonRelationships()){
1050 if (taxon
.getTaxonRelations().size() > 0){
1051 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1052 "Remove taxon from all relations to other taxa prior to deletion.";
1053 // throw new ReferencedObjectUndeletableException(message);
1056 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1057 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1058 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1059 if (taxon
.equals(taxRel
.getToTaxon())){
1060 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1064 taxon
.removeTaxonRelation(taxRel
);
1065 /*if (taxFrom.equals(taxon)){
1067 this.deleteTaxon(taxTo, taxConf, classification);
1068 } catch(DataChangeNoRollbackException e){
1069 logger.debug("A related taxon will not be deleted." + e.getMessage());
1073 this.deleteTaxon(taxFrom, taxConf, classification);
1074 } catch(DataChangeNoRollbackException e){
1075 logger.debug("A related taxon will not be deleted." + e.getMessage());
1083 if (config
.isDeleteDescriptions()){
1084 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1085 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1086 for (TaxonDescription desc
: descriptions
){
1087 //TODO use description delete configurator ?
1088 //FIXME check if description is ALWAYS deletable
1089 if (desc
.getDescribedSpecimenOrObservation() != null){
1090 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1091 " which also describes specimens or abservations";
1092 //throw new ReferencedObjectUndeletableException(message);
1094 removeDescriptions
.add(desc
);
1095 descriptionService
.delete(desc
);
1098 for (TaxonDescription desc
: removeDescriptions
){
1099 taxon
.removeDescription(desc
);
1104 /* //check references with only reverse mapping
1105 String message = checkForReferences(taxon);
1106 if (message != null){
1107 //throw new ReferencedObjectUndeletableException(message.toString());
1110 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1111 //if (taxon.getTaxonNodes().size() > 0){
1112 // 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.";
1113 // throw new ReferencedObjectUndeletableException(message);
1116 if (taxon
.getTaxonNodes().size() != 0){
1117 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1118 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1119 TaxonNode node
= null;
1120 boolean deleteChildren
;
1121 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1122 deleteChildren
= true;
1124 deleteChildren
= false;
1126 boolean success
= true;
1127 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1128 while (iterator
.hasNext()){
1129 node
= iterator
.next();
1130 if (node
.getClassification().equals(classification
)){
1136 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1137 nodeService
.delete(node
);
1139 // message = "Taxon is not used in defined classification";
1140 // throw new DataChangeNoRollbackException(message);
1142 } else if (config
.isDeleteInAllClassifications()){
1143 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1144 nodesList
.addAll(taxon
.getTaxonNodes());
1146 for (ITaxonTreeNode treeNode
: nodesList
){
1147 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1148 if(!deleteChildren
){
1149 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1150 //nodesList.addAll(taxonNode.getChildNodes());
1151 for (Object childNode: childNodes){
1152 TaxonNode childNodeCast = (TaxonNode) childNode;
1153 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1157 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1158 deleteTaxon(childNode.getTaxon(), config, classification);
1161 // taxon.removeTaxonNode(taxonNode);
1162 //nodeService.delete(taxonNode);
1165 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1166 for (Object childNode
: childNodes
){
1167 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1168 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1171 //taxon.removeTaxonNode(taxonNode);
1174 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1175 nodeService
.deleteTaxonNodes(nodesList
, config
);
1178 // message = "The taxon node could not be deleted.";
1179 //throw new DataChangeNoRollbackException(message);
1185 //PolytomousKey TODO
1187 boolean usedInPolytomousKey
= checkForPolytomousKeys(taxon
);
1189 if (config
.isDeleteNameIfPossible()){
1192 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1193 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1194 //check whether taxon will be deleted or not
1195 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1196 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1197 name
.removeTaxonBase(taxon
);
1198 nameService
.save(name
);
1199 String uuidString
= nameService
.delete(name
, config
.getNameDeletionConfig());
1200 logger
.debug(uuidString
);
1206 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1208 for (TaxonDescription desc: descriptions){
1209 if (config.isDeleteDescriptions()){
1210 //TODO use description delete configurator ?
1211 //FIXME check if description is ALWAYS deletable
1212 taxon.removeDescription(desc);
1213 descriptionService.delete(desc);
1215 if (desc.getDescribedSpecimenOrObservations().size()>0){
1216 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1217 " which also describes specimens or observations";
1218 throw new ReferencedObjectUndeletableException(message);
1223 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1224 UUID uuid
= dao
.delete(taxon
);
1225 return uuid
.toString();
1227 return "The Taxon can't be deleted.";
1230 return referencedObjects
.toString();
1236 private String
checkForReferences(Taxon taxon
){
1237 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1238 for (CdmBase referencingObject
: referencingObjects
){
1239 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1240 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1241 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";
1247 /* //PolytomousKeyNode
1248 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1249 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1254 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1255 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1260 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1261 String message
= "Taxon can't be deleted as it is used in a determination event";
1267 referencingObjects
= null;
1271 private boolean checkForPolytomousKeys(Taxon taxon
){
1272 boolean result
= false;
1273 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1274 if (!list
.isEmpty()) {
1280 @Transactional(readOnly
= false)
1281 public UUID
delete(Synonym syn
){
1282 UUID result
= syn
.getUuid();
1283 this.deleteSynonym(syn
, null);
1288 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1290 @Transactional(readOnly
= false)
1292 public String
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1293 return deleteSynonym(synonym
, null, config
);
1299 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1301 @Transactional(readOnly
= false)
1303 public String
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1304 if (synonym
== null){
1308 if (config
== null){
1309 config
= new SynonymDeletionConfigurator();
1311 List
<String
> messages
= isDeletable(synonym
, config
);
1312 if (messages
.isEmpty()){
1313 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1315 //remove synonymRelationship
1316 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1318 taxonSet
.add(taxon
);
1320 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1322 for (Taxon relatedTaxon
: taxonSet
){
1323 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1324 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1326 this.saveOrUpdate(synonym
);
1328 //TODO remove name from homotypical group?
1330 //remove synonym (if necessary)
1333 if (synonym
.getSynonymRelations().isEmpty()){
1334 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1335 synonym
.setName(null);
1336 uuid
= dao
.delete(synonym
);
1338 //remove name if possible (and required)
1339 if (name
!= null && config
.isDeleteNameIfPossible()){
1341 nameService
.delete(name
, config
.getNameDeletionConfig());
1348 return uuid
.toString();
1350 return messages
.toString();
1358 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1361 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1363 return this.dao
.findIdenticalNamesNew(propertyPath
);
1367 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1370 public String
getPhylumName(TaxonNameBase name
){
1371 return this.dao
.getPhylumName(name
);
1375 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1378 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1379 return dao
.deleteSynonymRelationships(syn
, taxon
);
1383 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1386 public long deleteSynonymRelationships(Synonym syn
) {
1387 return dao
.deleteSynonymRelationships(syn
, null);
1392 * @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)
1395 public List
<SynonymRelationship
> listSynonymRelationships(
1396 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1397 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1398 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1400 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1401 if(numberOfResults
> 0) { // no point checking again
1402 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1408 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1411 public Taxon
findBestMatchingTaxon(String taxonName
) {
1412 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1413 config
.setTaxonNameTitle(taxonName
);
1414 return findBestMatchingTaxon(config
);
1420 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1422 Taxon bestCandidate
= null;
1424 // 1. search for acceptet taxa
1425 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1426 boolean bestCandidateMatchesSecUuid
= false;
1427 boolean bestCandidateIsInClassification
= false;
1428 int countEqualCandidates
= 0;
1429 for(TaxonBase taxonBaseCandidate
: taxonList
){
1430 if(taxonBaseCandidate
instanceof Taxon
){
1431 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1432 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1433 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1435 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1436 bestCandidate
= newCanditate
;
1437 countEqualCandidates
= 1;
1438 bestCandidateMatchesSecUuid
= true;
1442 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1443 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1445 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1446 bestCandidate
= newCanditate
;
1447 countEqualCandidates
= 1;
1448 bestCandidateIsInClassification
= true;
1451 if (bestCandidate
== null){
1452 bestCandidate
= newCanditate
;
1453 countEqualCandidates
= 1;
1457 }else{ //not Taxon.class
1460 countEqualCandidates
++;
1463 if (bestCandidate
!= null){
1464 if(countEqualCandidates
> 1){
1465 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1466 return bestCandidate
;
1468 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1469 return bestCandidate
;
1474 // 2. search for synonyms
1475 if (config
.isIncludeSynonyms()){
1476 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1477 for(TaxonBase taxonBase
: synonymList
){
1478 if(taxonBase
instanceof Synonym
){
1479 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1480 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1481 if(!acceptetdCandidates
.isEmpty()){
1482 bestCandidate
= acceptetdCandidates
.iterator().next();
1483 if(acceptetdCandidates
.size() == 1){
1484 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1485 return bestCandidate
;
1487 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1488 return bestCandidate
;
1490 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1496 } catch (Exception e
){
1498 e
.printStackTrace();
1501 return bestCandidate
;
1504 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1505 UUID configClassificationUuid
= config
.getClassificationUuid();
1506 if (configClassificationUuid
== null){
1509 for (TaxonNode node
: taxon
.getTaxonNodes()){
1510 UUID classUuid
= node
.getClassification().getUuid();
1511 if (configClassificationUuid
.equals(classUuid
)){
1518 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1519 UUID configSecUuid
= config
.getSecUuid();
1520 if (configSecUuid
== null){
1523 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1524 return configSecUuid
.equals(taxonSecUuid
);
1528 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1531 public Synonym
findBestMatchingSynonym(String taxonName
) {
1532 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1533 if(! synonymList
.isEmpty()){
1534 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1535 if(synonymList
.size() == 1){
1536 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1539 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1548 * @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)
1551 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1552 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1554 Synonym synonym
= oldSynonymRelation
.getSynonym();
1555 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1556 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1557 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1558 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1559 //set default relationship type
1560 if (newSynonymRelationshipType
== null){
1561 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1563 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1565 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1566 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1567 boolean isSingleInGroup
= !(hgSize
> 1);
1569 if (! isSingleInGroup
){
1570 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1571 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1572 if (isHomotypicToAccepted
){
1573 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.";
1574 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1575 message
= String
.format(message
, homotypicRelatives
);
1576 throw new HomotypicalGroupChangeException(message
);
1578 if (! moveHomotypicGroup
){
1579 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.";
1580 throw new HomotypicalGroupChangeException(message
);
1583 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1585 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1587 SynonymRelationship result
= null;
1588 //move all synonyms to new taxon
1589 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1590 for (Synonym syn
: homotypicSynonyms
){
1591 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1592 for (SynonymRelationship synRelation
: synRelations
){
1593 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1594 Reference
<?
> newReference
= reference
;
1595 if (newReference
== null && keepReference
){
1596 newReference
= synRelation
.getCitation();
1598 String newRefDetail
= referenceDetail
;
1599 if (newRefDetail
== null && keepReference
){
1600 newRefDetail
= synRelation
.getCitationMicroReference();
1602 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1603 fromTaxon
.removeSynonymRelation(synRelation
, false);
1605 //change homotypic group of synonym if relType is 'homotypic'
1606 // if (newRelTypeIsHomotypic){
1607 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1610 if (synRelation
.equals(oldSynonymRelation
)){
1611 result
= newSynRelation
;
1617 saveOrUpdate(newTaxon
);
1618 //Assert that there is a result
1619 if (result
== null){
1620 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1621 throw new IllegalStateException(message
);
1627 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1630 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1631 return dao
.getUuidAndTitleCacheTaxon();
1635 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1638 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1639 return dao
.getUuidAndTitleCacheSynonym();
1643 * @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)
1646 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1647 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1648 Classification classification
, List
<Language
> languages
,
1649 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1652 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1654 // --- execute search
1655 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1657 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1658 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1660 // --- initialize taxa, thighlight matches ....
1661 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1662 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1663 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1665 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1666 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1670 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1671 Classification classification
,
1672 Integer pageSize
, Integer pageNumber
,
1673 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1675 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1677 // --- execute search
1678 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1680 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1681 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1683 // --- initialize taxa, thighlight matches ....
1684 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1685 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1686 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1688 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1689 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1694 * @param queryString
1695 * @param classification
1697 * @param highlightFragments
1698 * @param sortFields TODO
1699 * @param directorySelectClass
1702 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1703 boolean highlightFragments
, SortField
[] sortFields
) {
1704 BooleanQuery finalQuery
= new BooleanQuery();
1705 BooleanQuery textQuery
= new BooleanQuery();
1707 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1708 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1710 if(sortFields
== null){
1711 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1713 luceneSearch
.setSortFields(sortFields
);
1715 // ---- search criteria
1716 luceneSearch
.setCdmTypRestriction(clazz
);
1718 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1719 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1721 finalQuery
.add(textQuery
, Occur
.MUST
);
1723 if(classification
!= null){
1724 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1726 luceneSearch
.setQuery(finalQuery
);
1728 if(highlightFragments
){
1729 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1731 return luceneSearch
;
1735 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1736 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1737 * drawback of requiring to do the join an indexing time.
1738 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1740 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1742 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1743 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1745 * @param queryString
1746 * @param classification
1748 * @param highlightFragments
1749 * @param sortFields TODO
1752 * @throws IOException
1754 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1755 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1758 String queryTermField
;
1759 String toField
= "id"; // TaxonBase.uuid
1761 if(edge
.isBidirectional()){
1762 throw new RuntimeException("Bidirectional joining not supported!");
1765 fromField
= "relatedFrom.id";
1766 queryTermField
= "relatedFrom.titleCache";
1767 } else if(edge
.isInvers()) {
1768 fromField
= "relatedTo.id";
1769 queryTermField
= "relatedTo.titleCache";
1771 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1774 BooleanQuery finalQuery
= new BooleanQuery();
1776 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1777 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1779 BooleanQuery joinFromQuery
= new BooleanQuery();
1780 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1781 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1782 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1784 if(sortFields
== null){
1785 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1787 luceneSearch
.setSortFields(sortFields
);
1789 finalQuery
.add(joinQuery
, Occur
.MUST
);
1791 if(classification
!= null){
1792 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1794 luceneSearch
.setQuery(finalQuery
);
1796 if(highlightFragments
){
1797 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1799 return luceneSearch
;
1806 * @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)
1809 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1810 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1811 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1812 boolean highlightFragments
, Integer pageSize
,
1813 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1814 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1816 // FIXME: allow taxonomic ordering
1817 // 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";
1818 // this require building a special sort column by a special classBridge
1819 if(highlightFragments
){
1820 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1821 "currently not fully supported by this method and thus " +
1822 "may not work with common names and misapplied names.");
1825 // convert sets to lists
1826 List
<NamedArea
> namedAreaList
= null;
1827 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1828 if(namedAreas
!= null){
1829 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1830 namedAreaList
.addAll(namedAreas
);
1832 if(distributionStatus
!= null){
1833 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1834 distributionStatusList
.addAll(distributionStatus
);
1837 // set default if parameter is null
1838 if(searchModes
== null){
1839 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1842 // set sort order and thus override any sort orders which may have been
1843 // defindes by prepare*Search methods
1844 if(orderHints
== null){
1845 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1847 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1849 for(OrderHint oh
: orderHints
){
1850 sortFields
[i
++] = oh
.toSortField();
1852 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1853 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1856 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1858 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1859 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1862 ======== filtering by distribution , HOWTO ========
1864 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1865 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1866 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1867 which will be put into a FilteredQuersy in the end ?
1870 3. how does it work in spatial?
1872 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1873 - http://www.infoq.com/articles/LuceneSpatialSupport
1874 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1875 ------------------------------------------------------------------------
1878 A) use a separate distribution filter per index sub-query/search:
1879 - byTaxonSyonym (query TaxaonBase):
1880 use a join area filter (Distribution -> TaxonBase)
1881 - byCommonName (query DescriptionElementBase): use an area filter on
1882 DescriptionElementBase !!! PROBLEM !!!
1883 This cannot work since the distributions are different entities than the
1884 common names and thus these are different lucene documents.
1885 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1886 use a join area filter (Distribution -> TaxonBase)
1888 B) use a common distribution filter for all index sub-query/searches:
1889 - use a common join area filter (Distribution -> TaxonBase)
1890 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1891 PROBLEM in this case: we are losing the fragment highlighting for the
1892 common names, since the returned documents are always TaxonBases
1895 /* The QueryFactory for creating filter queries on Distributions should
1896 * The query factory used for the common names query cannot be reused
1897 * for this case, since we want to only record the text fields which are
1898 * actually used in the primary query
1900 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1902 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1905 // search for taxa or synonyms
1906 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1907 Class taxonBaseSubclass
= TaxonBase
.class;
1908 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1909 taxonBaseSubclass
= Taxon
.class;
1910 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1911 taxonBaseSubclass
= Synonym
.class;
1913 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1914 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1915 /* A) does not work!!!!
1916 if(addDistributionFilter){
1917 // in this case we need a filter which uses a join query
1918 // to get the TaxonBase documents for the DescriptionElementBase documents
1919 // which are matching the areas in question
1920 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1922 distributionStatusList,
1923 distributionFilterQueryFactory
1925 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1928 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1929 // add additional area filter for synonyms
1930 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1931 String toField
= "accTaxon.id"; // id in TaxonBase index
1933 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1935 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1936 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1941 // search by CommonTaxonName
1942 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1944 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1945 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1946 "inDescription.taxon.id",
1948 QueryFactory
.addTypeRestriction(
1949 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1950 , CommonTaxonName
.class
1952 CommonTaxonName
.class);
1953 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1954 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1955 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1956 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1957 byCommonNameSearch
.setSortFields(sortFields
);
1958 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1960 luceneSearches
.add(byCommonNameSearch
);
1962 /* A) does not work!!!!
1964 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1965 queryString, classification, null, languages, highlightFragments)
1967 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1968 if(addDistributionFilter){
1969 // in this case we are able to use DescriptionElementBase documents
1970 // which are matching the areas in question directly
1971 BooleanQuery byDistributionQuery = createByDistributionQuery(
1973 distributionStatusList,
1974 distributionFilterQueryFactory
1976 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1980 // search by misapplied names
1981 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1983 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1984 // which allows doing query time joins
1985 // finds the misapplied name (Taxon B) which is an misapplication for
1986 // a related Taxon A.
1988 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1989 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1990 queryString
, classification
, languages
, highlightFragments
, sortFields
));
1991 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1993 if(addDistributionFilter
){
1994 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1997 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1998 * Maybe this is a but in java itself java.
2000 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2003 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2005 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2006 * will execute as expected:
2008 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2009 * String toField = "relation." + misappliedNameForUuid +".to.id";
2011 * Comparing both strings by the String.equals method returns true, so both String are identical.
2013 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2014 * 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)
2015 * The bug is persistent after a reboot of the development computer.
2017 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2018 // String toField = "relation." + misappliedNameForUuid +".to.id";
2019 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2020 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2021 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2023 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2024 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2025 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2027 // debug code for bug described above
2028 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2029 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2031 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2035 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2036 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2039 if(addDistributionFilter
){
2042 // in this case we need a filter which uses a join query
2043 // to get the TaxonBase documents for the DescriptionElementBase documents
2044 // which are matching the areas in question
2046 // for toTaxa, doByCommonName
2047 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2049 distributionStatusList
,
2050 distributionFilterQueryFactory
2052 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2055 if (addDistributionFilter
){
2056 multiSearch
.setFilter(multiIndexByAreaFilter
);
2060 // --- execute search
2061 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2063 // --- initialize taxa, highlight matches ....
2064 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2067 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2068 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2070 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2071 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2075 * @param namedAreaList at least one area must be in the list
2076 * @param distributionStatusList optional
2078 * @throws IOException
2080 protected Query
createByDistributionJoinQuery(
2081 List
<NamedArea
> namedAreaList
,
2082 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2083 QueryFactory queryFactory
2084 ) throws IOException
{
2086 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2087 String toField
= "id"; // id in TaxonBase index
2089 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2091 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2093 return taxonAreaJoinQuery
;
2097 * @param namedAreaList
2098 * @param distributionStatusList
2099 * @param queryFactory
2102 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2103 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
2104 BooleanQuery areaQuery
= new BooleanQuery();
2105 // area field from Distribution
2106 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2108 // status field from Distribution
2109 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2110 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2113 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2118 * This method has been primarily created for testing the area join query but might
2119 * also be useful in other situations
2121 * @param namedAreaList
2122 * @param distributionStatusList
2123 * @param classification
2124 * @param highlightFragments
2126 * @throws IOException
2128 protected LuceneSearch
prepareByDistributionSearch(
2129 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2130 Classification classification
) throws IOException
{
2132 BooleanQuery finalQuery
= new BooleanQuery();
2134 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2136 // FIXME is this query factory using the wrong type?
2137 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2139 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2140 luceneSearch
.setSortFields(sortFields
);
2143 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2145 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2147 if(classification
!= null){
2148 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2151 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2152 luceneSearch
.setQuery(finalQuery
);
2154 return luceneSearch
;
2160 * @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)
2163 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2164 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2165 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2166 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2169 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2171 // --- execute search
2172 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2174 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2175 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2177 // --- initialize taxa, highlight matches ....
2178 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2179 @SuppressWarnings("rawtypes")
2180 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2181 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2183 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2184 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2190 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2191 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2192 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2194 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2195 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2197 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2199 // --- execute search
2200 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2202 // --- initialize taxa, highlight matches ....
2203 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2205 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2206 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2207 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2209 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2210 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2212 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2213 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2220 * @param queryString
2221 * @param classification
2224 * @param highlightFragments
2225 * @param directorySelectClass
2228 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2229 String queryString
, Classification classification
, List
<Feature
> features
,
2230 List
<Language
> languages
, boolean highlightFragments
) {
2232 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2233 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2235 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2237 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2238 languages
, descriptionElementQueryFactory
);
2240 luceneSearch
.setSortFields(sortFields
);
2241 luceneSearch
.setCdmTypRestriction(clazz
);
2242 luceneSearch
.setQuery(finalQuery
);
2243 if(highlightFragments
){
2244 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2247 return luceneSearch
;
2251 * @param queryString
2252 * @param classification
2255 * @param descriptionElementQueryFactory
2258 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2259 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2260 BooleanQuery finalQuery
= new BooleanQuery();
2261 BooleanQuery textQuery
= new BooleanQuery();
2262 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2266 if(languages
== null || languages
.size() == 0){
2267 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2269 nameQuery
= new BooleanQuery();
2270 BooleanQuery languageSubQuery
= new BooleanQuery();
2271 for(Language lang
: languages
){
2272 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2274 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2275 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2277 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2280 // text field from TextData
2281 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2283 // --- TermBase fields - by representation ----
2284 // state field from CategoricalData
2285 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2287 // state field from CategoricalData
2288 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2290 // area field from Distribution
2291 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2293 // status field from Distribution
2294 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2296 finalQuery
.add(textQuery
, Occur
.MUST
);
2297 // --- classification ----
2299 if(classification
!= null){
2300 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2303 // --- IdentifieableEntity fields - by uuid
2304 if(features
!= null && features
.size() > 0 ){
2305 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2308 // the description must be associated with a taxon
2309 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2311 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2316 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2317 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2318 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2320 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2321 * or {@link MultilanguageTextFieldBridge }
2322 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2323 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2324 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2326 * TODO move to utiliy class !!!!!!!!
2328 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2330 if(stringBuilder
== null){
2331 stringBuilder
= new StringBuilder();
2333 if(languages
== null || languages
.size() == 0){
2334 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2336 for(Language lang
: languages
){
2337 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2340 return stringBuilder
;
2344 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2345 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2346 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2348 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2351 UUID nameUuid
= taxon
.getName().getUuid();
2352 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2353 String epithetOfTaxon
= null;
2354 String infragenericEpithetOfTaxon
= null;
2355 String infraspecificEpithetOfTaxon
= null;
2356 if (taxonName
.isSpecies()){
2357 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2358 } else if (taxonName
.isInfraGeneric()){
2359 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2360 } else if (taxonName
.isInfraSpecific()){
2361 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2363 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2364 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2365 List
<String
> taxonNames
= new ArrayList
<String
>();
2367 for (TaxonNode node
: nodes
){
2368 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2369 // List<String> synonymsEpithet = new ArrayList<String>();
2371 if (node
.getClassification().equals(classification
)){
2372 if (!node
.isTopmostNode()){
2373 TaxonNode parent
= node
.getParent();
2374 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2375 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2376 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2377 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2378 Rank rankOfTaxon
= taxonName
.getRank();
2381 //create inferred synonyms for species, subspecies
2382 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2384 Synonym inferredEpithet
= null;
2385 Synonym inferredGenus
= null;
2386 Synonym potentialCombination
= null;
2388 List
<String
> propertyPaths
= new ArrayList
<String
>();
2389 propertyPaths
.add("synonym");
2390 propertyPaths
.add("synonym.name");
2391 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2392 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2394 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2395 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2397 List
<TaxonRelationship
> taxonRelListParent
= null;
2398 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2399 if (doWithMisappliedNames
){
2400 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2401 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2405 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2408 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2409 Synonym syn
= synonymRelationOfParent
.getSynonym();
2411 inferredEpithet
= createInferredEpithets(taxon
,
2412 zooHashMap
, taxonName
, epithetOfTaxon
,
2413 infragenericEpithetOfTaxon
,
2414 infraspecificEpithetOfTaxon
,
2415 taxonNames
, parentName
,
2419 inferredSynonyms
.add(inferredEpithet
);
2420 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2421 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2424 if (doWithMisappliedNames
){
2426 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2427 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2429 inferredEpithet
= createInferredEpithets(taxon
,
2430 zooHashMap
, taxonName
, epithetOfTaxon
,
2431 infragenericEpithetOfTaxon
,
2432 infraspecificEpithetOfTaxon
,
2433 taxonNames
, parentName
,
2436 inferredSynonyms
.add(inferredEpithet
);
2437 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2438 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2442 if (!taxonNames
.isEmpty()){
2443 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2444 ZoologicalName name
;
2445 if (!synNotInCDM
.isEmpty()){
2446 inferredSynonymsToBeRemoved
.clear();
2448 for (Synonym syn
:inferredSynonyms
){
2449 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2450 if (!synNotInCDM
.contains(name
.getNameCache())){
2451 inferredSynonymsToBeRemoved
.add(syn
);
2455 // Remove identified Synonyms from inferredSynonyms
2456 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2457 inferredSynonyms
.remove(synonym
);
2462 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2465 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2466 TaxonNameBase synName
;
2467 ZoologicalName inferredSynName
;
2469 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2470 inferredGenus
= createInferredGenus(taxon
,
2471 zooHashMap
, taxonName
, epithetOfTaxon
,
2472 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2474 inferredSynonyms
.add(inferredGenus
);
2475 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2476 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2481 if (doWithMisappliedNames
){
2483 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2484 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2485 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2487 inferredSynonyms
.add(inferredGenus
);
2488 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2489 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2494 if (!taxonNames
.isEmpty()){
2495 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2496 ZoologicalName name
;
2497 if (!synNotInCDM
.isEmpty()){
2498 inferredSynonymsToBeRemoved
.clear();
2500 for (Synonym syn
:inferredSynonyms
){
2501 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2502 if (!synNotInCDM
.contains(name
.getNameCache())){
2503 inferredSynonymsToBeRemoved
.add(syn
);
2507 // Remove identified Synonyms from inferredSynonyms
2508 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2509 inferredSynonyms
.remove(synonym
);
2514 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2516 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2517 ZoologicalName inferredSynName
;
2518 //for all synonyms of the parent...
2519 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2520 TaxonNameBase synName
;
2521 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2522 synName
= synParent
.getName();
2524 HibernateProxyHelper
.deproxy(synParent
);
2526 // Set the sourceReference
2527 sourceReference
= synParent
.getSec();
2529 // Determine the idInSource
2530 String idInSourceParent
= getIdInSource(synParent
);
2532 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2533 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2534 String synParentInfragenericName
= null;
2535 String synParentSpecificEpithet
= null;
2537 if (parentSynZooName
.isInfraGeneric()){
2538 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2540 if (parentSynZooName
.isSpecies()){
2541 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2544 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2545 synonymsGenus.put(synGenusName, idInSource);
2548 //for all synonyms of the taxon
2550 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2552 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2553 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2554 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2556 synParentInfragenericName
,
2557 synParentSpecificEpithet
, syn
, zooHashMap
);
2559 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2560 inferredSynonyms
.add(potentialCombination
);
2561 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2562 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2569 if (doWithMisappliedNames
){
2571 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2573 TaxonNameBase misappliedParentName
;
2575 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2576 misappliedParentName
= misappliedParent
.getName();
2578 HibernateProxyHelper
.deproxy(misappliedParent
);
2580 // Set the sourceReference
2581 sourceReference
= misappliedParent
.getSec();
2583 // Determine the idInSource
2584 String idInSourceParent
= getIdInSource(misappliedParent
);
2586 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2587 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2588 String synParentInfragenericName
= null;
2589 String synParentSpecificEpithet
= null;
2591 if (parentSynZooName
.isInfraGeneric()){
2592 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2594 if (parentSynZooName
.isSpecies()){
2595 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2599 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2600 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2601 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2602 potentialCombination
= createPotentialCombination(
2603 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2605 synParentInfragenericName
,
2606 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2609 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2610 inferredSynonyms
.add(potentialCombination
);
2611 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2612 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2617 if (!taxonNames
.isEmpty()){
2618 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2619 ZoologicalName name
;
2620 if (!synNotInCDM
.isEmpty()){
2621 inferredSynonymsToBeRemoved
.clear();
2622 for (Synonym syn
:inferredSynonyms
){
2624 name
= (ZoologicalName
) syn
.getName();
2625 }catch (ClassCastException e
){
2626 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2628 if (!synNotInCDM
.contains(name
.getNameCache())){
2629 inferredSynonymsToBeRemoved
.add(syn
);
2632 // Remove identified Synonyms from inferredSynonyms
2633 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2634 inferredSynonyms
.remove(synonym
);
2640 logger
.info("The synonymrelationship type is not defined.");
2641 return inferredSynonyms
;
2648 return inferredSynonyms
;
2651 private Synonym
createPotentialCombination(String idInSourceParent
,
2652 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2653 String synParentInfragenericName
, String synParentSpecificEpithet
,
2654 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2655 Synonym potentialCombination
;
2656 Reference sourceReference
;
2657 ZoologicalName inferredSynName
;
2658 HibernateProxyHelper
.deproxy(syn
);
2660 // Set sourceReference
2661 sourceReference
= syn
.getSec();
2662 if (sourceReference
== null){
2663 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2665 if (!parentSynZooName
.getTaxa().isEmpty()){
2666 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2668 sourceReference
= taxon
.getSec();
2671 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2673 String synTaxonInfraSpecificName
= null;
2675 if (parentSynZooName
.isSpecies()){
2676 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2679 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2680 synonymsEpithet.add(epithetName);
2683 //create potential combinations...
2684 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2686 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2687 if (zooSynName
.isSpecies()){
2688 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2689 if (parentSynZooName
.isInfraGeneric()){
2690 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2693 if (zooSynName
.isInfraSpecific()){
2694 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2695 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2697 if (parentSynZooName
.isInfraGeneric()){
2698 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2702 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2704 // Set the sourceReference
2705 potentialCombination
.setSec(sourceReference
);
2708 // Determine the idInSource
2709 String idInSourceSyn
= getIdInSource(syn
);
2711 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2712 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2713 inferredSynName
.addSource(originalSource
);
2714 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2715 potentialCombination
.addSource(originalSource
);
2718 inferredSynName
.generateTitle();
2720 return potentialCombination
;
2723 private Synonym
createInferredGenus(Taxon taxon
,
2724 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2725 String epithetOfTaxon
, String genusOfTaxon
,
2726 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2729 Synonym inferredGenus
;
2730 TaxonNameBase synName
;
2731 ZoologicalName inferredSynName
;
2732 synName
=syn
.getName();
2733 HibernateProxyHelper
.deproxy(syn
);
2735 // Determine the idInSource
2736 String idInSourceSyn
= getIdInSource(syn
);
2737 String idInSourceTaxon
= getIdInSource(taxon
);
2738 // Determine the sourceReference
2739 Reference sourceReference
= syn
.getSec();
2741 //logger.warn(sourceReference.getTitleCache());
2743 synName
= syn
.getName();
2744 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2745 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2746 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2747 synonymsEpithet.add(synSpeciesEpithetName);
2750 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2751 //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...
2754 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2755 if (zooParentName
.isInfraGeneric()){
2756 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2759 if (taxonName
.isSpecies()){
2760 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2762 if (taxonName
.isInfraSpecific()){
2763 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2764 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2768 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2770 // Set the sourceReference
2771 inferredGenus
.setSec(sourceReference
);
2773 // Add the original source
2774 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2775 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2776 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2777 inferredGenus
.addSource(originalSource
);
2779 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2780 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2781 inferredSynName
.addSource(originalSource
);
2782 originalSource
= null;
2785 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2786 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2787 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2788 inferredGenus
.addSource(originalSource
);
2790 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2791 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2792 inferredSynName
.addSource(originalSource
);
2793 originalSource
= null;
2796 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2798 inferredSynName
.generateTitle();
2801 return inferredGenus
;
2804 private Synonym
createInferredEpithets(Taxon taxon
,
2805 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2806 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2807 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2808 TaxonNameBase parentName
, TaxonBase syn
) {
2810 Synonym inferredEpithet
;
2811 TaxonNameBase
<?
,?
> synName
;
2812 ZoologicalName inferredSynName
;
2813 HibernateProxyHelper
.deproxy(syn
);
2815 // Determine the idInSource
2816 String idInSourceSyn
= getIdInSource(syn
);
2817 String idInSourceTaxon
= getIdInSource(taxon
);
2818 // Determine the sourceReference
2819 Reference
<?
> sourceReference
= syn
.getSec();
2821 if (sourceReference
== null){
2822 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2823 sourceReference
= taxon
.getSec();
2826 synName
= syn
.getName();
2827 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2828 String synGenusName
= zooSynName
.getGenusOrUninomial();
2829 String synInfraGenericEpithet
= null;
2830 String synSpecificEpithet
= null;
2832 if (zooSynName
.getInfraGenericEpithet() != null){
2833 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2836 if (zooSynName
.isInfraSpecific()){
2837 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2840 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2841 synonymsGenus.put(synGenusName, idInSource);
2844 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2846 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2847 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2848 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2850 inferredSynName
.setGenusOrUninomial(synGenusName
);
2852 if (parentName
.isInfraGeneric()){
2853 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2855 if (taxonName
.isSpecies()){
2856 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2857 }else if (taxonName
.isInfraSpecific()){
2858 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2859 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2862 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2864 // Set the sourceReference
2865 inferredEpithet
.setSec(sourceReference
);
2867 /* Add the original source
2868 if (idInSource != null) {
2869 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2872 Reference citation = getCitation(syn);
2873 if (citation != null) {
2874 originalSource.setCitation(citation);
2875 inferredEpithet.addSource(originalSource);
2878 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2881 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2882 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2884 inferredEpithet
.addSource(originalSource
);
2886 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2887 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2889 inferredSynName
.addSource(originalSource
);
2893 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2895 inferredSynName
.generateTitle();
2896 return inferredEpithet
;
2900 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2901 * Very likely only useful for createInferredSynonyms().
2906 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2907 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2908 if (taxonName
== null) {
2909 taxonName
= zooHashMap
.get(uuid
);
2915 * Returns the idInSource for a given Synonym.
2918 private String
getIdInSource(TaxonBase taxonBase
) {
2919 String idInSource
= null;
2920 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2921 if (sources
.size() == 1) {
2922 IdentifiableSource source
= sources
.iterator().next();
2923 if (source
!= null) {
2924 idInSource
= source
.getIdInSource();
2926 } else if (sources
.size() > 1) {
2929 for (IdentifiableSource source
: sources
) {
2930 idInSource
+= source
.getIdInSource();
2931 if (count
< sources
.size()) {
2936 } else if (sources
.size() == 0){
2937 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2946 * Returns the citation for a given Synonym.
2949 private Reference
getCitation(Synonym syn
) {
2950 Reference citation
= null;
2951 Set
<IdentifiableSource
> sources
= syn
.getSources();
2952 if (sources
.size() == 1) {
2953 IdentifiableSource source
= sources
.iterator().next();
2954 if (source
!= null) {
2955 citation
= source
.getCitation();
2957 } else if (sources
.size() > 1) {
2958 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2965 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2966 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2968 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2969 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2970 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2972 return inferredSynonyms
;
2976 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2978 // TODO quickly implemented, create according dao !!!!
2979 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2980 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2981 List
<Classification
> list
= new ArrayList
<Classification
>();
2983 if (taxonBase
== null) {
2987 taxonBase
= load(taxonBase
.getUuid());
2989 if (taxonBase
instanceof Taxon
) {
2990 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2992 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2993 nodes
.addAll(taxon
.getTaxonNodes());
2996 for (TaxonNode node
: nodes
) {
2997 classifications
.add(node
.getClassification());
2999 list
.addAll(classifications
);
3004 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3005 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3006 // Create new synonym using concept name
3007 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3008 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3010 // Remove concept relation from taxon
3011 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3016 // Create a new synonym for the taxon
3017 SynonymRelationship synonymRelationship
;
3018 if (synonymRelationshipType
!= null
3019 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3020 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3022 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3025 this.saveOrUpdate(toTaxon
);
3026 //TODO: configurator and classification
3027 this.deleteTaxon(fromTaxon
, null, null);
3028 return synonymRelationship
.getSynonym();
3032 public List
<String
> isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3033 List
<String
> result
= new ArrayList
<String
>();
3034 Set
<CdmBase
> references
= commonService
.getReferencingObjects(taxonBase
);
3035 if (taxonBase
instanceof Taxon
){
3036 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3037 result
= isDeletableForTaxon(references
, taxonConfig
);
3039 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3040 result
= isDeletableForSynonym(references
, synonymConfig
);
3045 private List
<String
> isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3047 List
<String
> result
= new ArrayList
<String
>();
3048 for (CdmBase ref
: references
){
3049 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3050 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3051 result
.add(message
);
3057 private List
<String
> isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3059 List
<String
> result
= new ArrayList
<String
>();
3060 for (CdmBase ref
: references
){
3061 if (!(ref
instanceof TaxonNameBase
)){
3062 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3063 message
= "The Taxon can't be deleted as long as it has synonyms.";
3064 result
.add(message
);
3066 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3067 message
= "The Taxon can't be deleted as long as it has factual data.";
3068 result
.add(message
);
3071 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3072 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3073 result
.add(message
);
3075 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3076 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3077 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3078 result
.add(message
);
3080 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3081 result
.add(message
);
3084 if (ref
instanceof PolytomousKeyNode
){
3085 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3086 result
.add(message
);
3089 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3090 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3091 result
.add(message
);
3096 /* //PolytomousKeyNode
3097 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3098 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3103 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3104 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3105 result
.add(message
);
3109 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3110 message
= "Taxon can't be deleted as it is used in a determination event";
3111 result
.add(message
);
3122 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3123 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3125 //preliminary implementation
3127 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3128 TaxonBase taxonBase
= find(taxonUuid
);
3129 if (taxonBase
== null){
3130 return new IncludedTaxaDTO();
3131 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3132 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3134 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3135 //TODO partial synonyms ??
3136 //TODO synonyms in general
3137 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3138 taxa
.addAll(syn
.getAcceptedTaxa());
3140 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3143 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3145 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3146 related
= makeRelatedIncluded(related
, result
, config
);
3153 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3155 * @return the set of conceptually related taxa for further use
3158 * @param uncheckedTaxa
3159 * @param existingTaxa
3163 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3166 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3167 for (Taxon taxon
: uncheckedTaxa
){
3168 taxonNodes
.addAll(taxon
.getTaxonNodes());
3171 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3172 if (! config
.onlyCongruent
){
3173 for (TaxonNode node
: taxonNodes
){
3174 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true);
3175 for (TaxonNode child
: childNodes
){
3176 children
.add(child
.getTaxon());
3179 children
.remove(null); // just to be on the save side
3182 Iterator
<Taxon
> it
= children
.iterator();
3183 while(it
.hasNext()){
3184 UUID uuid
= it
.next().getUuid();
3185 if (existingTaxa
.contains(uuid
)){
3188 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3193 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3194 uncheckedAndChildren
.addAll(children
);
3196 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3199 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3204 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3205 * @return the set of these computed taxa
3207 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3208 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3210 for (Taxon taxon
: unchecked
){
3211 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3212 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3214 for (TaxonRelationship fromRel
: fromRelations
){
3215 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3218 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3219 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3220 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3222 result
.add(fromRel
.getToTaxon());
3226 for (TaxonRelationship toRel
: toRelations
){
3227 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3230 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3231 result
.add(toRel
.getFromTaxon());
3236 Iterator
<Taxon
> it
= result
.iterator();
3237 while(it
.hasNext()){
3238 UUID uuid
= it
.next().getUuid();
3239 if (existingTaxa
.contains(uuid
)){
3242 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);