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
<Taxon
> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
795 taxa
.addAll(commonNameResults
);
798 results
.addAll(taxa
);
800 numberOfResults
+= numberTaxaResults
;
804 return new DefaultPagerImpl
<IdentifiableEntity
>
805 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
808 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
809 return dao
.getUuidAndTitleCache();
813 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
816 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
817 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
818 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
819 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
820 for (TaxonDescription taxDesc
: descriptions
){
821 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
822 for (DescriptionElementBase descElem
: elements
){
823 for(Media media
: descElem
.getMedia()){
825 //find the best matching representation
826 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
835 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
838 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
839 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
844 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
847 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
848 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
849 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
851 logger
.trace("listMedia() - START");
853 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
854 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
856 if (limitToGalleries
== null) {
857 limitToGalleries
= false;
860 // --- resolve related taxa
861 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
862 logger
.trace("listMedia() - resolve related taxa");
863 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
866 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
868 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
869 logger
.trace("listMedia() - includeTaxonDescriptions");
870 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
871 // --- TaxonDescriptions
872 for (Taxon t
: taxa
) {
873 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
875 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
876 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
877 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
878 for (Media media
: element
.getMedia()) {
879 taxonMedia
.add(media
);
887 if(includeOccurrences
!= null && includeOccurrences
) {
888 logger
.trace("listMedia() - includeOccurrences");
889 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
891 for (Taxon t
: taxa
) {
892 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
894 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
896 // direct media removed from specimen #3597
897 // taxonMedia.addAll(occurrence.getMedia());
899 // SpecimenDescriptions
900 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
901 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
902 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
903 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
904 for (DescriptionElementBase element
: elements
) {
905 for (Media media
: element
.getMedia()) {
906 taxonMedia
.add(media
);
913 //TODO why may collections have media attached? #
914 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
915 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
916 if (derivedUnit
.getCollection() != null){
917 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
921 // pherograms & gelPhotos
922 if (occurrence
.isInstanceOf(DnaSample
.class)) {
923 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
924 Set
<Sequence
> sequences
= dnaSample
.getSequences();
925 //we do show only those gelPhotos which lead to a consensus sequence
926 for (Sequence sequence
: sequences
) {
927 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
928 for (SingleRead singleRead
: sequence
.getSingleReads()){
929 Amplification amplification
= singleRead
.getAmplification();
930 dnaRelatedMedia
.add(amplification
.getGelPhoto());
931 dnaRelatedMedia
.add(singleRead
.getPherogram());
932 dnaRelatedMedia
.remove(null);
934 taxonMedia
.addAll(dnaRelatedMedia
);
941 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
942 logger
.trace("listMedia() - includeTaxonNameDescriptions");
943 // --- TaxonNameDescription
944 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
945 for (Taxon t
: taxa
) {
946 nameDescriptions
.addAll(t
.getName().getDescriptions());
948 for(TaxonNameDescription nameDescription
: nameDescriptions
){
949 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
950 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
951 for (DescriptionElementBase element
: elements
) {
952 for (Media media
: element
.getMedia()) {
953 taxonMedia
.add(media
);
961 logger
.trace("listMedia() - initialize");
962 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
964 logger
.trace("listMedia() - END");
970 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
973 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
974 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
978 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
981 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
982 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
986 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
989 public int countAllRelationships() {
990 return this.dao
.countAllRelationships();
997 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
1000 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
1001 return this.dao
.findIdenticalTaxonNames(propertyPath
);
1006 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1009 public String
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
1010 if (config
== null){
1011 config
= new TaxonDeletionConfigurator();
1014 List
<String
> referencedObjects
= isDeletable(taxon
, config
);
1016 if (referencedObjects
.isEmpty()){
1017 // --- DeleteSynonymRelations
1018 if (config
.isDeleteSynonymRelations()){
1019 boolean removeSynonymNameFromHomotypicalGroup
= false;
1020 // use tmp Set to avoid concurrent modification
1021 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1022 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1023 for (SynonymRelationship synRel
: synRelsToDelete
){
1024 Synonym synonym
= synRel
.getSynonym();
1025 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1026 // this will cause hibernate to delete the relationship since
1027 // the SynonymRelationship field on both is annotated with removeOrphan
1028 // so no further explicit deleting of the relationship should be done here
1029 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1031 // --- DeleteSynonymsIfPossible
1032 if (config
.isDeleteSynonymsIfPossible()){
1034 boolean newHomotypicGroupIfNeeded
= true;
1035 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1036 deleteSynonym(synonym
, taxon
, synConfig
);
1038 // relationship will be deleted by hibernate automatically,
1039 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1041 // deleteSynonymRelationships(synonym, taxon);
1046 // --- DeleteTaxonRelationships
1047 if (! config
.isDeleteTaxonRelationships()){
1048 if (taxon
.getTaxonRelations().size() > 0){
1049 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1050 "Remove taxon from all relations to other taxa prior to deletion.";
1051 // throw new ReferencedObjectUndeletableException(message);
1054 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1055 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1056 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1057 if (taxon
.equals(taxRel
.getToTaxon())){
1058 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1062 taxon
.removeTaxonRelation(taxRel
);
1063 /*if (taxFrom.equals(taxon)){
1065 this.deleteTaxon(taxTo, taxConf, classification);
1066 } catch(DataChangeNoRollbackException e){
1067 logger.debug("A related taxon will not be deleted." + e.getMessage());
1071 this.deleteTaxon(taxFrom, taxConf, classification);
1072 } catch(DataChangeNoRollbackException e){
1073 logger.debug("A related taxon will not be deleted." + e.getMessage());
1081 if (config
.isDeleteDescriptions()){
1082 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1083 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1084 for (TaxonDescription desc
: descriptions
){
1085 //TODO use description delete configurator ?
1086 //FIXME check if description is ALWAYS deletable
1087 if (desc
.getDescribedSpecimenOrObservation() != null){
1088 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1089 " which also describes specimens or abservations";
1090 //throw new ReferencedObjectUndeletableException(message);
1092 removeDescriptions
.add(desc
);
1093 descriptionService
.delete(desc
);
1096 for (TaxonDescription desc
: removeDescriptions
){
1097 taxon
.removeDescription(desc
);
1102 /* //check references with only reverse mapping
1103 String message = checkForReferences(taxon);
1104 if (message != null){
1105 //throw new ReferencedObjectUndeletableException(message.toString());
1108 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1109 //if (taxon.getTaxonNodes().size() > 0){
1110 // 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.";
1111 // throw new ReferencedObjectUndeletableException(message);
1114 if (taxon
.getTaxonNodes().size() != 0){
1115 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1116 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1117 TaxonNode node
= null;
1118 boolean deleteChildren
;
1119 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1120 deleteChildren
= true;
1122 deleteChildren
= false;
1124 boolean success
= true;
1125 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1126 while (iterator
.hasNext()){
1127 node
= iterator
.next();
1128 if (node
.getClassification().equals(classification
)){
1134 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1135 nodeService
.delete(node
);
1137 // message = "Taxon is not used in defined classification";
1138 // throw new DataChangeNoRollbackException(message);
1140 } else if (config
.isDeleteInAllClassifications()){
1141 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1142 nodesList
.addAll(taxon
.getTaxonNodes());
1144 for (ITaxonTreeNode treeNode
: nodesList
){
1145 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1146 if(!deleteChildren
){
1147 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1148 //nodesList.addAll(taxonNode.getChildNodes());
1149 for (Object childNode: childNodes){
1150 TaxonNode childNodeCast = (TaxonNode) childNode;
1151 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1155 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1156 deleteTaxon(childNode.getTaxon(), config, classification);
1159 // taxon.removeTaxonNode(taxonNode);
1160 //nodeService.delete(taxonNode);
1163 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1164 for (Object childNode
: childNodes
){
1165 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1166 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1169 //taxon.removeTaxonNode(taxonNode);
1172 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1173 nodeService
.deleteTaxonNodes(nodesList
, config
);
1176 // message = "The taxon node could not be deleted.";
1177 //throw new DataChangeNoRollbackException(message);
1183 //PolytomousKey TODO
1185 boolean usedInPolytomousKey
= checkForPolytomousKeys(taxon
);
1187 if (config
.isDeleteNameIfPossible()){
1190 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1191 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1192 //check whether taxon will be deleted or not
1193 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1194 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1195 name
.removeTaxonBase(taxon
);
1196 nameService
.merge(name
);
1197 String uuidString
= nameService
.delete(name
, config
.getNameDeletionConfig());
1198 logger
.debug(uuidString
);
1204 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1206 for (TaxonDescription desc: descriptions){
1207 if (config.isDeleteDescriptions()){
1208 //TODO use description delete configurator ?
1209 //FIXME check if description is ALWAYS deletable
1210 taxon.removeDescription(desc);
1211 descriptionService.delete(desc);
1213 if (desc.getDescribedSpecimenOrObservations().size()>0){
1214 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1215 " which also describes specimens or observations";
1216 throw new ReferencedObjectUndeletableException(message);
1221 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1222 UUID uuid
= dao
.delete(taxon
);
1223 return uuid
.toString();
1225 return "The Taxon can't be deleted.";
1228 return referencedObjects
.toString();
1234 private String
checkForReferences(Taxon taxon
){
1235 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1236 for (CdmBase referencingObject
: referencingObjects
){
1237 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1238 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1239 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";
1245 /* //PolytomousKeyNode
1246 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1247 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1252 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1253 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1258 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1259 String message
= "Taxon can't be deleted as it is used in a determination event";
1265 referencingObjects
= null;
1269 private boolean checkForPolytomousKeys(Taxon taxon
){
1270 boolean result
= false;
1271 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1272 if (!list
.isEmpty()) {
1278 @Transactional(readOnly
= false)
1279 public UUID
delete(Synonym syn
){
1280 UUID result
= syn
.getUuid();
1281 this.deleteSynonym(syn
, null);
1286 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1288 @Transactional(readOnly
= false)
1290 public String
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1291 return deleteSynonym(synonym
, null, config
);
1297 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1299 @Transactional(readOnly
= false)
1301 public String
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1302 if (synonym
== null){
1306 if (config
== null){
1307 config
= new SynonymDeletionConfigurator();
1309 List
<String
> messages
= isDeletable(synonym
, config
);
1310 if (messages
.isEmpty()){
1311 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1313 //remove synonymRelationship
1314 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1316 taxonSet
.add(taxon
);
1318 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1320 for (Taxon relatedTaxon
: taxonSet
){
1321 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1322 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1324 this.saveOrUpdate(synonym
);
1326 //TODO remove name from homotypical group?
1328 //remove synonym (if necessary)
1331 if (synonym
.getSynonymRelations().isEmpty()){
1332 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1333 synonym
.setName(null);
1334 uuid
= dao
.delete(synonym
);
1336 //remove name if possible (and required)
1337 if (name
!= null && config
.isDeleteNameIfPossible()){
1339 nameService
.delete(name
, config
.getNameDeletionConfig());
1346 return uuid
.toString();
1348 return messages
.toString();
1356 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1359 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1361 return this.dao
.findIdenticalNamesNew(propertyPath
);
1365 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1368 public String
getPhylumName(TaxonNameBase name
){
1369 return this.dao
.getPhylumName(name
);
1373 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1376 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1377 return dao
.deleteSynonymRelationships(syn
, taxon
);
1381 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1384 public long deleteSynonymRelationships(Synonym syn
) {
1385 return dao
.deleteSynonymRelationships(syn
, null);
1390 * @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)
1393 public List
<SynonymRelationship
> listSynonymRelationships(
1394 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1395 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1396 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1398 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1399 if(numberOfResults
> 0) { // no point checking again
1400 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1406 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1409 public Taxon
findBestMatchingTaxon(String taxonName
) {
1410 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1411 config
.setTaxonNameTitle(taxonName
);
1412 return findBestMatchingTaxon(config
);
1418 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1420 Taxon bestCandidate
= null;
1422 // 1. search for acceptet taxa
1423 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1424 boolean bestCandidateMatchesSecUuid
= false;
1425 boolean bestCandidateIsInClassification
= false;
1426 int countEqualCandidates
= 0;
1427 for(TaxonBase taxonBaseCandidate
: taxonList
){
1428 if(taxonBaseCandidate
instanceof Taxon
){
1429 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1430 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1431 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1433 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1434 bestCandidate
= newCanditate
;
1435 countEqualCandidates
= 1;
1436 bestCandidateMatchesSecUuid
= true;
1440 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1441 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1443 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1444 bestCandidate
= newCanditate
;
1445 countEqualCandidates
= 1;
1446 bestCandidateIsInClassification
= true;
1449 if (bestCandidate
== null){
1450 bestCandidate
= newCanditate
;
1451 countEqualCandidates
= 1;
1455 }else{ //not Taxon.class
1458 countEqualCandidates
++;
1461 if (bestCandidate
!= null){
1462 if(countEqualCandidates
> 1){
1463 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1464 return bestCandidate
;
1466 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1467 return bestCandidate
;
1472 // 2. search for synonyms
1473 if (config
.isIncludeSynonyms()){
1474 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1475 for(TaxonBase taxonBase
: synonymList
){
1476 if(taxonBase
instanceof Synonym
){
1477 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1478 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1479 if(!acceptetdCandidates
.isEmpty()){
1480 bestCandidate
= acceptetdCandidates
.iterator().next();
1481 if(acceptetdCandidates
.size() == 1){
1482 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1483 return bestCandidate
;
1485 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1486 return bestCandidate
;
1488 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1494 } catch (Exception e
){
1496 e
.printStackTrace();
1499 return bestCandidate
;
1502 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1503 UUID configClassificationUuid
= config
.getClassificationUuid();
1504 if (configClassificationUuid
== null){
1507 for (TaxonNode node
: taxon
.getTaxonNodes()){
1508 UUID classUuid
= node
.getClassification().getUuid();
1509 if (configClassificationUuid
.equals(classUuid
)){
1516 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1517 UUID configSecUuid
= config
.getSecUuid();
1518 if (configSecUuid
== null){
1521 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1522 return configSecUuid
.equals(taxonSecUuid
);
1526 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1529 public Synonym
findBestMatchingSynonym(String taxonName
) {
1530 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1531 if(! synonymList
.isEmpty()){
1532 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1533 if(synonymList
.size() == 1){
1534 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1537 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1546 * @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)
1549 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1550 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1552 Synonym synonym
= oldSynonymRelation
.getSynonym();
1553 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1554 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1555 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1556 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1557 //set default relationship type
1558 if (newSynonymRelationshipType
== null){
1559 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1561 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1563 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1564 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1565 boolean isSingleInGroup
= !(hgSize
> 1);
1567 if (! isSingleInGroup
){
1568 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1569 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1570 if (isHomotypicToAccepted
){
1571 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.";
1572 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1573 message
= String
.format(message
, homotypicRelatives
);
1574 throw new HomotypicalGroupChangeException(message
);
1576 if (! moveHomotypicGroup
){
1577 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.";
1578 throw new HomotypicalGroupChangeException(message
);
1581 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1583 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1585 SynonymRelationship result
= null;
1586 //move all synonyms to new taxon
1587 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1588 for (Synonym syn
: homotypicSynonyms
){
1589 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1590 for (SynonymRelationship synRelation
: synRelations
){
1591 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1592 Reference
<?
> newReference
= reference
;
1593 if (newReference
== null && keepReference
){
1594 newReference
= synRelation
.getCitation();
1596 String newRefDetail
= referenceDetail
;
1597 if (newRefDetail
== null && keepReference
){
1598 newRefDetail
= synRelation
.getCitationMicroReference();
1600 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1601 fromTaxon
.removeSynonymRelation(synRelation
, false);
1603 //change homotypic group of synonym if relType is 'homotypic'
1604 // if (newRelTypeIsHomotypic){
1605 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1608 if (synRelation
.equals(oldSynonymRelation
)){
1609 result
= newSynRelation
;
1615 saveOrUpdate(newTaxon
);
1616 //Assert that there is a result
1617 if (result
== null){
1618 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1619 throw new IllegalStateException(message
);
1625 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1628 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1629 return dao
.getUuidAndTitleCacheTaxon();
1633 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1636 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1637 return dao
.getUuidAndTitleCacheSynonym();
1641 * @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)
1644 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1645 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1646 Classification classification
, List
<Language
> languages
,
1647 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1650 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1652 // --- execute search
1653 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1655 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1656 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1658 // --- initialize taxa, thighlight matches ....
1659 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1660 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1661 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1663 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1664 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1668 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1669 Classification classification
,
1670 Integer pageSize
, Integer pageNumber
,
1671 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1673 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1675 // --- execute search
1676 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1678 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1679 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1681 // --- initialize taxa, thighlight matches ....
1682 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1683 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1684 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1686 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1687 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1692 * @param queryString
1693 * @param classification
1695 * @param highlightFragments
1696 * @param sortFields TODO
1697 * @param directorySelectClass
1700 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1701 boolean highlightFragments
, SortField
[] sortFields
) {
1702 BooleanQuery finalQuery
= new BooleanQuery();
1703 BooleanQuery textQuery
= new BooleanQuery();
1705 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1706 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1708 if(sortFields
== null){
1709 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1711 luceneSearch
.setSortFields(sortFields
);
1713 // ---- search criteria
1714 luceneSearch
.setCdmTypRestriction(clazz
);
1716 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1717 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1719 finalQuery
.add(textQuery
, Occur
.MUST
);
1721 if(classification
!= null){
1722 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1724 luceneSearch
.setQuery(finalQuery
);
1726 if(highlightFragments
){
1727 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1729 return luceneSearch
;
1733 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1734 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1735 * drawback of requiring to do the join an indexing time.
1736 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1738 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1740 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1741 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1743 * @param queryString
1744 * @param classification
1746 * @param highlightFragments
1747 * @param sortFields TODO
1750 * @throws IOException
1752 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1753 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1756 String queryTermField
;
1757 String toField
= "id"; // TaxonBase.uuid
1759 if(edge
.isBidirectional()){
1760 throw new RuntimeException("Bidirectional joining not supported!");
1763 fromField
= "relatedFrom.id";
1764 queryTermField
= "relatedFrom.titleCache";
1765 } else if(edge
.isInvers()) {
1766 fromField
= "relatedTo.id";
1767 queryTermField
= "relatedTo.titleCache";
1769 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1772 BooleanQuery finalQuery
= new BooleanQuery();
1774 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1775 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1777 BooleanQuery joinFromQuery
= new BooleanQuery();
1778 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1779 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1780 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1782 if(sortFields
== null){
1783 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1785 luceneSearch
.setSortFields(sortFields
);
1787 finalQuery
.add(joinQuery
, Occur
.MUST
);
1789 if(classification
!= null){
1790 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1792 luceneSearch
.setQuery(finalQuery
);
1794 if(highlightFragments
){
1795 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1797 return luceneSearch
;
1804 * @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)
1807 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1808 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1809 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1810 boolean highlightFragments
, Integer pageSize
,
1811 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1812 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1814 // FIXME: allow taxonomic ordering
1815 // 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";
1816 // this require building a special sort column by a special classBridge
1817 if(highlightFragments
){
1818 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1819 "currently not fully supported by this method and thus " +
1820 "may not work with common names and misapplied names.");
1823 // convert sets to lists
1824 List
<NamedArea
> namedAreaList
= null;
1825 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1826 if(namedAreas
!= null){
1827 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1828 namedAreaList
.addAll(namedAreas
);
1830 if(distributionStatus
!= null){
1831 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1832 distributionStatusList
.addAll(distributionStatus
);
1835 // set default if parameter is null
1836 if(searchModes
== null){
1837 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1840 // set sort order and thus override any sort orders which may have been
1841 // defindes by prepare*Search methods
1842 if(orderHints
== null){
1843 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1845 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1847 for(OrderHint oh
: orderHints
){
1848 sortFields
[i
++] = oh
.toSortField();
1850 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1851 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1854 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1856 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1857 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1860 ======== filtering by distribution , HOWTO ========
1862 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1863 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1864 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1865 which will be put into a FilteredQuersy in the end ?
1868 3. how does it work in spatial?
1870 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1871 - http://www.infoq.com/articles/LuceneSpatialSupport
1872 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1873 ------------------------------------------------------------------------
1876 A) use a separate distribution filter per index sub-query/search:
1877 - byTaxonSyonym (query TaxaonBase):
1878 use a join area filter (Distribution -> TaxonBase)
1879 - byCommonName (query DescriptionElementBase): use an area filter on
1880 DescriptionElementBase !!! PROBLEM !!!
1881 This cannot work since the distributions are different entities than the
1882 common names and thus these are different lucene documents.
1883 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1884 use a join area filter (Distribution -> TaxonBase)
1886 B) use a common distribution filter for all index sub-query/searches:
1887 - use a common join area filter (Distribution -> TaxonBase)
1888 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1889 PROBLEM in this case: we are losing the fragment highlighting for the
1890 common names, since the returned documents are always TaxonBases
1893 /* The QueryFactory for creating filter queries on Distributions should
1894 * The query factory used for the common names query cannot be reused
1895 * for this case, since we want to only record the text fields which are
1896 * actually used in the primary query
1898 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1900 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1903 // search for taxa or synonyms
1904 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1905 Class taxonBaseSubclass
= TaxonBase
.class;
1906 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1907 taxonBaseSubclass
= Taxon
.class;
1908 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1909 taxonBaseSubclass
= Synonym
.class;
1911 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1912 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1913 /* A) does not work!!!!
1914 if(addDistributionFilter){
1915 // in this case we need a filter which uses a join query
1916 // to get the TaxonBase documents for the DescriptionElementBase documents
1917 // which are matching the areas in question
1918 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1920 distributionStatusList,
1921 distributionFilterQueryFactory
1923 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1926 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1927 // add additional area filter for synonyms
1928 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1929 String toField
= "accTaxon.id"; // id in TaxonBase index
1931 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1933 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1934 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1939 // search by CommonTaxonName
1940 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1942 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1943 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1944 "inDescription.taxon.id",
1946 QueryFactory
.addTypeRestriction(
1947 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1948 , CommonTaxonName
.class
1950 CommonTaxonName
.class);
1951 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1952 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1953 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1954 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1955 byCommonNameSearch
.setSortFields(sortFields
);
1956 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1958 luceneSearches
.add(byCommonNameSearch
);
1960 /* A) does not work!!!!
1962 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1963 queryString, classification, null, languages, highlightFragments)
1965 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1966 if(addDistributionFilter){
1967 // in this case we are able to use DescriptionElementBase documents
1968 // which are matching the areas in question directly
1969 BooleanQuery byDistributionQuery = createByDistributionQuery(
1971 distributionStatusList,
1972 distributionFilterQueryFactory
1974 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1978 // search by misapplied names
1979 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1981 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1982 // which allows doing query time joins
1983 // finds the misapplied name (Taxon B) which is an misapplication for
1984 // a related Taxon A.
1986 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1987 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1988 queryString
, classification
, languages
, highlightFragments
, sortFields
));
1989 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1991 if(addDistributionFilter
){
1992 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1995 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1996 * Maybe this is a but in java itself java.
1998 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2001 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2003 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2004 * will execute as expected:
2006 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2007 * String toField = "relation." + misappliedNameForUuid +".to.id";
2009 * Comparing both strings by the String.equals method returns true, so both String are identical.
2011 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2012 * 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)
2013 * The bug is persistent after a reboot of the development computer.
2015 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2016 // String toField = "relation." + misappliedNameForUuid +".to.id";
2017 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2018 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2019 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2021 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2022 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2023 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2025 // debug code for bug described above
2026 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2027 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2029 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2033 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2034 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2037 if(addDistributionFilter
){
2040 // in this case we need a filter which uses a join query
2041 // to get the TaxonBase documents for the DescriptionElementBase documents
2042 // which are matching the areas in question
2044 // for toTaxa, doByCommonName
2045 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2047 distributionStatusList
,
2048 distributionFilterQueryFactory
2050 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2053 if (addDistributionFilter
){
2054 multiSearch
.setFilter(multiIndexByAreaFilter
);
2058 // --- execute search
2059 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2061 // --- initialize taxa, highlight matches ....
2062 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2065 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2066 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2068 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2069 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2073 * @param namedAreaList at least one area must be in the list
2074 * @param distributionStatusList optional
2076 * @throws IOException
2078 protected Query
createByDistributionJoinQuery(
2079 List
<NamedArea
> namedAreaList
,
2080 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2081 QueryFactory queryFactory
2082 ) throws IOException
{
2084 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2085 String toField
= "id"; // id in TaxonBase index
2087 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2089 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2091 return taxonAreaJoinQuery
;
2095 * @param namedAreaList
2096 * @param distributionStatusList
2097 * @param queryFactory
2100 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2101 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
2102 BooleanQuery areaQuery
= new BooleanQuery();
2103 // area field from Distribution
2104 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2106 // status field from Distribution
2107 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2108 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2111 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2116 * This method has been primarily created for testing the area join query but might
2117 * also be useful in other situations
2119 * @param namedAreaList
2120 * @param distributionStatusList
2121 * @param classification
2122 * @param highlightFragments
2124 * @throws IOException
2126 protected LuceneSearch
prepareByDistributionSearch(
2127 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2128 Classification classification
) throws IOException
{
2130 BooleanQuery finalQuery
= new BooleanQuery();
2132 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2134 // FIXME is this query factory using the wrong type?
2135 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2137 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2138 luceneSearch
.setSortFields(sortFields
);
2141 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2143 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2145 if(classification
!= null){
2146 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2149 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2150 luceneSearch
.setQuery(finalQuery
);
2152 return luceneSearch
;
2158 * @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)
2161 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2162 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2163 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2164 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2167 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2169 // --- execute search
2170 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2172 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2173 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2175 // --- initialize taxa, highlight matches ....
2176 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2177 @SuppressWarnings("rawtypes")
2178 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2179 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2181 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2182 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2188 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2189 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2190 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2192 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2193 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2195 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2197 // --- execute search
2198 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2200 // --- initialize taxa, highlight matches ....
2201 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2203 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2204 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2205 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2207 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2208 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2210 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2211 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2218 * @param queryString
2219 * @param classification
2222 * @param highlightFragments
2223 * @param directorySelectClass
2226 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2227 String queryString
, Classification classification
, List
<Feature
> features
,
2228 List
<Language
> languages
, boolean highlightFragments
) {
2230 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2231 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2233 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2235 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2236 languages
, descriptionElementQueryFactory
);
2238 luceneSearch
.setSortFields(sortFields
);
2239 luceneSearch
.setCdmTypRestriction(clazz
);
2240 luceneSearch
.setQuery(finalQuery
);
2241 if(highlightFragments
){
2242 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2245 return luceneSearch
;
2249 * @param queryString
2250 * @param classification
2253 * @param descriptionElementQueryFactory
2256 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2257 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2258 BooleanQuery finalQuery
= new BooleanQuery();
2259 BooleanQuery textQuery
= new BooleanQuery();
2260 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2264 if(languages
== null || languages
.size() == 0){
2265 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2267 nameQuery
= new BooleanQuery();
2268 BooleanQuery languageSubQuery
= new BooleanQuery();
2269 for(Language lang
: languages
){
2270 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2272 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2273 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2275 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2278 // text field from TextData
2279 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2281 // --- TermBase fields - by representation ----
2282 // state field from CategoricalData
2283 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2285 // state field from CategoricalData
2286 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2288 // area field from Distribution
2289 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2291 // status field from Distribution
2292 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2294 finalQuery
.add(textQuery
, Occur
.MUST
);
2295 // --- classification ----
2297 if(classification
!= null){
2298 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2301 // --- IdentifieableEntity fields - by uuid
2302 if(features
!= null && features
.size() > 0 ){
2303 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2306 // the description must be associated with a taxon
2307 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2309 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2314 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2315 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2316 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2318 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2319 * or {@link MultilanguageTextFieldBridge }
2320 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2321 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2322 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2324 * TODO move to utiliy class !!!!!!!!
2326 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2328 if(stringBuilder
== null){
2329 stringBuilder
= new StringBuilder();
2331 if(languages
== null || languages
.size() == 0){
2332 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2334 for(Language lang
: languages
){
2335 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2338 return stringBuilder
;
2342 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2343 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2344 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2346 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2349 UUID nameUuid
= taxon
.getName().getUuid();
2350 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2351 String epithetOfTaxon
= null;
2352 String infragenericEpithetOfTaxon
= null;
2353 String infraspecificEpithetOfTaxon
= null;
2354 if (taxonName
.isSpecies()){
2355 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2356 } else if (taxonName
.isInfraGeneric()){
2357 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2358 } else if (taxonName
.isInfraSpecific()){
2359 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2361 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2362 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2363 List
<String
> taxonNames
= new ArrayList
<String
>();
2365 for (TaxonNode node
: nodes
){
2366 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2367 // List<String> synonymsEpithet = new ArrayList<String>();
2369 if (node
.getClassification().equals(classification
)){
2370 if (!node
.isTopmostNode()){
2371 TaxonNode parent
= node
.getParent();
2372 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2373 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2374 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2375 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2376 Rank rankOfTaxon
= taxonName
.getRank();
2379 //create inferred synonyms for species, subspecies
2380 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2382 Synonym inferredEpithet
= null;
2383 Synonym inferredGenus
= null;
2384 Synonym potentialCombination
= null;
2386 List
<String
> propertyPaths
= new ArrayList
<String
>();
2387 propertyPaths
.add("synonym");
2388 propertyPaths
.add("synonym.name");
2389 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2390 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2392 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2393 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2395 List
<TaxonRelationship
> taxonRelListParent
= null;
2396 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2397 if (doWithMisappliedNames
){
2398 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2399 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2403 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2406 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2407 Synonym syn
= synonymRelationOfParent
.getSynonym();
2409 inferredEpithet
= createInferredEpithets(taxon
,
2410 zooHashMap
, taxonName
, epithetOfTaxon
,
2411 infragenericEpithetOfTaxon
,
2412 infraspecificEpithetOfTaxon
,
2413 taxonNames
, parentName
,
2417 inferredSynonyms
.add(inferredEpithet
);
2418 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2419 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2422 if (doWithMisappliedNames
){
2424 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2425 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2427 inferredEpithet
= createInferredEpithets(taxon
,
2428 zooHashMap
, taxonName
, epithetOfTaxon
,
2429 infragenericEpithetOfTaxon
,
2430 infraspecificEpithetOfTaxon
,
2431 taxonNames
, parentName
,
2434 inferredSynonyms
.add(inferredEpithet
);
2435 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2436 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2440 if (!taxonNames
.isEmpty()){
2441 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2442 ZoologicalName name
;
2443 if (!synNotInCDM
.isEmpty()){
2444 inferredSynonymsToBeRemoved
.clear();
2446 for (Synonym syn
:inferredSynonyms
){
2447 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2448 if (!synNotInCDM
.contains(name
.getNameCache())){
2449 inferredSynonymsToBeRemoved
.add(syn
);
2453 // Remove identified Synonyms from inferredSynonyms
2454 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2455 inferredSynonyms
.remove(synonym
);
2460 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2463 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2464 TaxonNameBase synName
;
2465 ZoologicalName inferredSynName
;
2467 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2468 inferredGenus
= createInferredGenus(taxon
,
2469 zooHashMap
, taxonName
, epithetOfTaxon
,
2470 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2472 inferredSynonyms
.add(inferredGenus
);
2473 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2474 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2479 if (doWithMisappliedNames
){
2481 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2482 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2483 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2485 inferredSynonyms
.add(inferredGenus
);
2486 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2487 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2492 if (!taxonNames
.isEmpty()){
2493 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2494 ZoologicalName name
;
2495 if (!synNotInCDM
.isEmpty()){
2496 inferredSynonymsToBeRemoved
.clear();
2498 for (Synonym syn
:inferredSynonyms
){
2499 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2500 if (!synNotInCDM
.contains(name
.getNameCache())){
2501 inferredSynonymsToBeRemoved
.add(syn
);
2505 // Remove identified Synonyms from inferredSynonyms
2506 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2507 inferredSynonyms
.remove(synonym
);
2512 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2514 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2515 ZoologicalName inferredSynName
;
2516 //for all synonyms of the parent...
2517 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2518 TaxonNameBase synName
;
2519 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2520 synName
= synParent
.getName();
2522 HibernateProxyHelper
.deproxy(synParent
);
2524 // Set the sourceReference
2525 sourceReference
= synParent
.getSec();
2527 // Determine the idInSource
2528 String idInSourceParent
= getIdInSource(synParent
);
2530 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2531 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2532 String synParentInfragenericName
= null;
2533 String synParentSpecificEpithet
= null;
2535 if (parentSynZooName
.isInfraGeneric()){
2536 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2538 if (parentSynZooName
.isSpecies()){
2539 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2542 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2543 synonymsGenus.put(synGenusName, idInSource);
2546 //for all synonyms of the taxon
2548 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2550 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2551 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2552 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2554 synParentInfragenericName
,
2555 synParentSpecificEpithet
, syn
, zooHashMap
);
2557 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2558 inferredSynonyms
.add(potentialCombination
);
2559 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2560 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2567 if (doWithMisappliedNames
){
2569 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2571 TaxonNameBase misappliedParentName
;
2573 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2574 misappliedParentName
= misappliedParent
.getName();
2576 HibernateProxyHelper
.deproxy(misappliedParent
);
2578 // Set the sourceReference
2579 sourceReference
= misappliedParent
.getSec();
2581 // Determine the idInSource
2582 String idInSourceParent
= getIdInSource(misappliedParent
);
2584 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2585 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2586 String synParentInfragenericName
= null;
2587 String synParentSpecificEpithet
= null;
2589 if (parentSynZooName
.isInfraGeneric()){
2590 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2592 if (parentSynZooName
.isSpecies()){
2593 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2597 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2598 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2599 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2600 potentialCombination
= createPotentialCombination(
2601 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2603 synParentInfragenericName
,
2604 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2607 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2608 inferredSynonyms
.add(potentialCombination
);
2609 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2610 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2615 if (!taxonNames
.isEmpty()){
2616 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2617 ZoologicalName name
;
2618 if (!synNotInCDM
.isEmpty()){
2619 inferredSynonymsToBeRemoved
.clear();
2620 for (Synonym syn
:inferredSynonyms
){
2622 name
= (ZoologicalName
) syn
.getName();
2623 }catch (ClassCastException e
){
2624 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2626 if (!synNotInCDM
.contains(name
.getNameCache())){
2627 inferredSynonymsToBeRemoved
.add(syn
);
2630 // Remove identified Synonyms from inferredSynonyms
2631 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2632 inferredSynonyms
.remove(synonym
);
2638 logger
.info("The synonymrelationship type is not defined.");
2639 return inferredSynonyms
;
2646 return inferredSynonyms
;
2649 private Synonym
createPotentialCombination(String idInSourceParent
,
2650 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2651 String synParentInfragenericName
, String synParentSpecificEpithet
,
2652 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2653 Synonym potentialCombination
;
2654 Reference sourceReference
;
2655 ZoologicalName inferredSynName
;
2656 HibernateProxyHelper
.deproxy(syn
);
2658 // Set sourceReference
2659 sourceReference
= syn
.getSec();
2660 if (sourceReference
== null){
2661 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2663 if (!parentSynZooName
.getTaxa().isEmpty()){
2664 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2666 sourceReference
= taxon
.getSec();
2669 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2671 String synTaxonInfraSpecificName
= null;
2673 if (parentSynZooName
.isSpecies()){
2674 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2677 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2678 synonymsEpithet.add(epithetName);
2681 //create potential combinations...
2682 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2684 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2685 if (zooSynName
.isSpecies()){
2686 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2687 if (parentSynZooName
.isInfraGeneric()){
2688 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2691 if (zooSynName
.isInfraSpecific()){
2692 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2693 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2695 if (parentSynZooName
.isInfraGeneric()){
2696 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2700 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2702 // Set the sourceReference
2703 potentialCombination
.setSec(sourceReference
);
2706 // Determine the idInSource
2707 String idInSourceSyn
= getIdInSource(syn
);
2709 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2710 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2711 inferredSynName
.addSource(originalSource
);
2712 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2713 potentialCombination
.addSource(originalSource
);
2716 return potentialCombination
;
2719 private Synonym
createInferredGenus(Taxon taxon
,
2720 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2721 String epithetOfTaxon
, String genusOfTaxon
,
2722 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2725 Synonym inferredGenus
;
2726 TaxonNameBase synName
;
2727 ZoologicalName inferredSynName
;
2728 synName
=syn
.getName();
2729 HibernateProxyHelper
.deproxy(syn
);
2731 // Determine the idInSource
2732 String idInSourceSyn
= getIdInSource(syn
);
2733 String idInSourceTaxon
= getIdInSource(taxon
);
2734 // Determine the sourceReference
2735 Reference sourceReference
= syn
.getSec();
2737 //logger.warn(sourceReference.getTitleCache());
2739 synName
= syn
.getName();
2740 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2741 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2742 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2743 synonymsEpithet.add(synSpeciesEpithetName);
2746 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2747 //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...
2750 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2751 if (zooParentName
.isInfraGeneric()){
2752 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2755 if (taxonName
.isSpecies()){
2756 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2758 if (taxonName
.isInfraSpecific()){
2759 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2760 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2764 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2766 // Set the sourceReference
2767 inferredGenus
.setSec(sourceReference
);
2769 // Add the original source
2770 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2771 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2772 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2773 inferredGenus
.addSource(originalSource
);
2775 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2776 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2777 inferredSynName
.addSource(originalSource
);
2778 originalSource
= null;
2781 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2782 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2783 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2784 inferredGenus
.addSource(originalSource
);
2786 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2787 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2788 inferredSynName
.addSource(originalSource
);
2789 originalSource
= null;
2792 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2794 return inferredGenus
;
2797 private Synonym
createInferredEpithets(Taxon taxon
,
2798 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2799 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2800 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2801 TaxonNameBase parentName
, TaxonBase syn
) {
2803 Synonym inferredEpithet
;
2804 TaxonNameBase
<?
,?
> synName
;
2805 ZoologicalName inferredSynName
;
2806 HibernateProxyHelper
.deproxy(syn
);
2808 // Determine the idInSource
2809 String idInSourceSyn
= getIdInSource(syn
);
2810 String idInSourceTaxon
= getIdInSource(taxon
);
2811 // Determine the sourceReference
2812 Reference
<?
> sourceReference
= syn
.getSec();
2814 if (sourceReference
== null){
2815 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2816 sourceReference
= taxon
.getSec();
2819 synName
= syn
.getName();
2820 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2821 String synGenusName
= zooSynName
.getGenusOrUninomial();
2822 String synInfraGenericEpithet
= null;
2823 String synSpecificEpithet
= null;
2825 if (zooSynName
.getInfraGenericEpithet() != null){
2826 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2829 if (zooSynName
.isInfraSpecific()){
2830 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2833 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2834 synonymsGenus.put(synGenusName, idInSource);
2837 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2839 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2840 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2841 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2843 inferredSynName
.setGenusOrUninomial(synGenusName
);
2845 if (parentName
.isInfraGeneric()){
2846 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2848 if (taxonName
.isSpecies()){
2849 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2850 }else if (taxonName
.isInfraSpecific()){
2851 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2852 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2855 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2857 // Set the sourceReference
2858 inferredEpithet
.setSec(sourceReference
);
2860 /* Add the original source
2861 if (idInSource != null) {
2862 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2865 Reference citation = getCitation(syn);
2866 if (citation != null) {
2867 originalSource.setCitation(citation);
2868 inferredEpithet.addSource(originalSource);
2871 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2874 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2875 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2877 inferredEpithet
.addSource(originalSource
);
2879 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2880 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2882 inferredSynName
.addSource(originalSource
);
2886 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2888 return inferredEpithet
;
2892 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2893 * Very likely only useful for createInferredSynonyms().
2898 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2899 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2900 if (taxonName
== null) {
2901 taxonName
= zooHashMap
.get(uuid
);
2907 * Returns the idInSource for a given Synonym.
2910 private String
getIdInSource(TaxonBase taxonBase
) {
2911 String idInSource
= null;
2912 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2913 if (sources
.size() == 1) {
2914 IdentifiableSource source
= sources
.iterator().next();
2915 if (source
!= null) {
2916 idInSource
= source
.getIdInSource();
2918 } else if (sources
.size() > 1) {
2921 for (IdentifiableSource source
: sources
) {
2922 idInSource
+= source
.getIdInSource();
2923 if (count
< sources
.size()) {
2928 } else if (sources
.size() == 0){
2929 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2938 * Returns the citation for a given Synonym.
2941 private Reference
getCitation(Synonym syn
) {
2942 Reference citation
= null;
2943 Set
<IdentifiableSource
> sources
= syn
.getSources();
2944 if (sources
.size() == 1) {
2945 IdentifiableSource source
= sources
.iterator().next();
2946 if (source
!= null) {
2947 citation
= source
.getCitation();
2949 } else if (sources
.size() > 1) {
2950 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2957 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2958 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2960 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2961 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2962 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2964 return inferredSynonyms
;
2968 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2970 // TODO quickly implemented, create according dao !!!!
2971 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2972 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2973 List
<Classification
> list
= new ArrayList
<Classification
>();
2975 if (taxonBase
== null) {
2979 taxonBase
= load(taxonBase
.getUuid());
2981 if (taxonBase
instanceof Taxon
) {
2982 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2984 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2985 nodes
.addAll(taxon
.getTaxonNodes());
2988 for (TaxonNode node
: nodes
) {
2989 classifications
.add(node
.getClassification());
2991 list
.addAll(classifications
);
2996 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
2997 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
2998 // Create new synonym using concept name
2999 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3000 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3002 // Remove concept relation from taxon
3003 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3008 // Create a new synonym for the taxon
3009 SynonymRelationship synonymRelationship
;
3010 if (synonymRelationshipType
!= null
3011 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3012 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3014 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3017 this.saveOrUpdate(toTaxon
);
3018 //TODO: configurator and classification
3019 this.deleteTaxon(fromTaxon
, null, null);
3020 return synonymRelationship
.getSynonym();
3024 public List
<String
> isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3025 List
<String
> result
= new ArrayList
<String
>();
3026 Set
<CdmBase
> references
= commonService
.getReferencingObjects(taxonBase
);
3027 if (taxonBase
instanceof Taxon
){
3028 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3029 result
= isDeletableForTaxon(references
, taxonConfig
);
3031 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3032 result
= isDeletableForSynonym(references
, synonymConfig
);
3037 private List
<String
> isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3039 List
<String
> result
= new ArrayList
<String
>();
3040 for (CdmBase ref
: references
){
3041 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3042 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3043 result
.add(message
);
3049 private List
<String
> isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3051 List
<String
> result
= new ArrayList
<String
>();
3052 for (CdmBase ref
: references
){
3053 if (!(ref
instanceof TaxonNameBase
)){
3054 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3055 message
= "The Taxon can't be deleted as long as it has synonyms.";
3056 result
.add(message
);
3058 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3059 message
= "The Taxon can't be deleted as long as it has factual data.";
3060 result
.add(message
);
3063 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3064 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3065 result
.add(message
);
3067 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3068 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3069 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3070 result
.add(message
);
3072 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3073 result
.add(message
);
3076 if (ref
instanceof PolytomousKeyNode
){
3077 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3078 result
.add(message
);
3081 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3082 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3083 result
.add(message
);
3088 /* //PolytomousKeyNode
3089 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3090 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3095 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3096 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3097 result
.add(message
);
3101 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3102 message
= "Taxon can't be deleted as it is used in a determination event";
3103 result
.add(message
);
3114 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3115 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3117 //preliminary implementation
3119 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3120 TaxonBase taxonBase
= find(taxonUuid
);
3121 if (taxonBase
== null){
3122 return new IncludedTaxaDTO();
3123 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3124 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3126 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3127 //TODO partial synonyms ??
3128 //TODO synonyms in general
3129 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3130 taxa
.addAll(syn
.getAcceptedTaxa());
3132 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3135 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3137 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3138 related
= makeRelatedIncluded(related
, result
, config
);
3145 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3147 * @return the set of conceptually related taxa for further use
3150 * @param uncheckedTaxa
3151 * @param existingTaxa
3155 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3158 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3159 for (Taxon taxon
: uncheckedTaxa
){
3160 taxonNodes
.addAll(taxon
.getTaxonNodes());
3163 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3164 if (! config
.onlyCongruent
){
3165 for (TaxonNode node
: taxonNodes
){
3166 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3167 for (TaxonNode child
: childNodes
){
3168 children
.add(child
.getTaxon());
3171 children
.remove(null); // just to be on the save side
3174 Iterator
<Taxon
> it
= children
.iterator();
3175 while(it
.hasNext()){
3176 UUID uuid
= it
.next().getUuid();
3177 if (existingTaxa
.contains(uuid
)){
3180 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3185 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3186 uncheckedAndChildren
.addAll(children
);
3188 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3191 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3196 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3197 * @return the set of these computed taxa
3199 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3200 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3202 for (Taxon taxon
: unchecked
){
3203 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3204 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3206 for (TaxonRelationship fromRel
: fromRelations
){
3207 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3210 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3211 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3212 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3214 result
.add(fromRel
.getToTaxon());
3218 for (TaxonRelationship toRel
: toRelations
){
3219 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3222 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3223 result
.add(toRel
.getFromTaxon());
3228 Iterator
<Taxon
> it
= result
.iterator();
3229 while(it
.hasNext()){
3230 UUID uuid
= it
.next().getUuid();
3231 if (existingTaxa
.contains(uuid
)){
3234 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);