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
.exception
.ReferencedObjectUndeletableException
;
51 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
52 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
61 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
62 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
63 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
64 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
65 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
66 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
67 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
68 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
69 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
70 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
71 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
72 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
73 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
74 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
75 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
76 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
77 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
78 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
79 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
80 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
81 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
82 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
83 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
84 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
85 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
86 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
87 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
88 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
89 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
90 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
91 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
92 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
93 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
94 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
95 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
96 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
97 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
98 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
99 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
100 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
101 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
102 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
103 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
104 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
105 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
106 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
108 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
115 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
116 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
117 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
118 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
119 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
122 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
123 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
124 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
125 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
126 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
127 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
128 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
132 * @author a.kohlbecker
137 @Transactional(readOnly
= true)
138 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
139 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
141 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
143 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
145 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
149 private ITaxonNameDao nameDao
;
152 private INameService nameService
;
155 private ITaxonNodeService nodeService
;
158 private ICdmGenericDao genericDao
;
161 private IDescriptionService descriptionService
;
164 private IOrderedTermVocabularyDao orderedVocabularyDao
;
167 private IOccurrenceDao occurrenceDao
;
170 private IClassificationDao classificationDao
;
173 private AbstractBeanInitializer beanInitializer
;
176 private ILuceneIndexToolProvider luceneIndexToolProvider
;
181 public TaxonServiceImpl(){
182 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
186 * FIXME Candidate for harmonization
187 * rename searchByName ?
190 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
191 return dao
.getTaxaByName(name
, sec
);
195 * FIXME Candidate for harmonization
196 * merge with getRootTaxa(Reference sec, ..., ...)
198 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
201 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
202 if (cdmFetch
== null){
203 cdmFetch
= CdmFetch
.NO_FETCH();
205 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
209 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
210 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
214 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
215 return dao
.getAllRelationships(limit
, start
);
219 * FIXME Candidate for harmonization
220 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
224 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
226 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
227 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
228 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
229 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
230 return taxonRelTypeVocabulary
;
237 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
240 @Transactional(readOnly
= false)
241 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
243 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
244 synonymName
.removeTaxonBase(synonym
);
245 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
246 taxonName
.removeTaxonBase(acceptedTaxon
);
248 synonym
.setName(taxonName
);
249 acceptedTaxon
.setName(synonymName
);
251 // the accepted taxon needs a new uuid because the concept has changed
252 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
253 //acceptedTaxon.setUuid(UUID.randomUUID());
258 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
262 @Transactional(readOnly
= false)
263 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
265 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
266 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
267 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
269 //check synonym is not homotypic
270 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
271 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
272 throw new HomotypicalGroupChangeException(message
);
275 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
277 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
278 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
279 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
281 for (Synonym heteroSynonym
: heteroSynonyms
){
282 if (synonym
.equals(heteroSynonym
)){
283 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
286 //move synonyms in same homotypic group to new accepted taxon
287 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
291 //synonym.getName().removeTaxonBase(synonym);
294 // deleteSynonym(synonym, taxon, false);
297 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
298 config
.setDeleteNameIfPossible(false);
299 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
301 } catch (Exception e
) {
302 logger
.info("Can't delete old synonym from database");
306 return newAcceptedTaxon
;
311 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
313 // Get name from synonym
314 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
316 /* // remove synonym from taxon
317 toTaxon.removeSynonym(synonym);
319 // Create a taxon with synonym name
320 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
322 // Add taxon relation
323 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
325 // since we are swapping names, we have to detach the name from the synonym completely.
326 // Otherwise the synonym will still be in the list of typified names.
327 // synonym.getName().removeTaxonBase(synonym);
328 this.deleteSynonym(synonym
, null);
335 * @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)
337 @Transactional(readOnly
= false)
339 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
340 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
342 TaxonNameBase synonymName
= synonym
.getName();
343 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
347 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
348 newHomotypicalGroup
.addTypifiedName(synonymName
);
350 //remove existing basionym relationships
351 synonymName
.removeBasionyms();
353 //add basionym relationship
354 if (setBasionymRelationIfApplicable
){
355 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
356 for (TaxonNameBase basionym
: basionyms
){
357 synonymName
.addBasionym(basionym
);
361 //set synonym relationship correctly
362 // SynonymRelationship relToTaxon = null;
363 boolean relToTargetTaxonExists
= false;
364 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
365 for (SynonymRelationship rel
: existingRelations
){
366 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
367 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
368 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
369 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
370 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
371 rel
.setType(newRelationType
);
372 //TODO handle citation and microCitation
375 relToTargetTaxonExists
= true;
377 if (removeFromOtherTaxa
){
378 acceptedTaxon
.removeSynonym(synonym
, false);
384 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
385 Taxon acceptedTaxon
= targetTaxon
;
386 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
387 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
388 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
389 //TODO handle citation and microCitation
390 Reference citation
= null;
391 String microCitation
= null;
392 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
399 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
402 @Transactional(readOnly
= false)
403 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
405 clazz
= TaxonBase
.class;
407 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
412 protected void setDao(ITaxonDao dao
) {
417 * @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)
420 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
421 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
423 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
424 if(numberOfResults
> 0) { // no point checking again
425 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
428 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
432 * @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)
435 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
436 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
438 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
439 if(numberOfResults
> 0) { // no point checking again
440 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
447 * @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)
450 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
451 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
453 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
454 if(numberOfResults
> 0) { // no point checking again
455 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
461 * @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)
464 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
465 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
467 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
468 if(numberOfResults
> 0) { // no point checking again
469 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
471 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
475 * @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)
478 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
479 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
481 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
482 if(numberOfResults
> 0) { // no point checking again
483 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
489 * @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)
492 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
493 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
495 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
496 if(numberOfResults
> 0) { // no point checking again
497 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
499 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
503 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
504 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
505 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
509 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
510 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
512 List
<Taxon
> list
= new ArrayList
<Taxon
>();
515 Synonym synonym
= null;
518 synonym
= (Synonym
) dao
.load(synonymUuid
);
519 } catch (ClassCastException e
){
520 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
521 } catch (NullPointerException e
){
522 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
525 Classification classificationFilter
= null;
526 if(classificationUuid
!= null){
528 classificationFilter
= classificationDao
.load(classificationUuid
);
529 } catch (NullPointerException e
){
530 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
532 if(classificationFilter
== null){
537 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
538 if(count
> (pageSize
* pageNumber
)){
539 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
542 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
548 * @param includeRelationships
552 * @param propertyPaths
553 * @return an List which is not specifically ordered
556 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
557 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
559 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
560 relatedTaxa
.remove(taxon
);
561 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
567 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
568 * <code>taxon</code> supplied as parameter.
571 * @param includeRelationships
573 * @param maxDepth can be <code>null</code> for infinite depth
576 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
582 if(includeRelationships
.isEmpty()){
586 if(maxDepth
!= null) {
589 if(logger
.isDebugEnabled()){
590 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
592 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
593 for (TaxonRelationship taxRel
: taxonRelationships
) {
596 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
599 // filter by includeRelationships
600 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
601 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
602 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
603 if(logger
.isDebugEnabled()){
604 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
606 taxa
.add(taxRel
.getToTaxon());
607 if(maxDepth
== null || maxDepth
> 0) {
608 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
611 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
612 taxa
.add(taxRel
.getFromTaxon());
613 if(logger
.isDebugEnabled()){
614 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
616 if(maxDepth
== null || maxDepth
> 0) {
617 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
627 * @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)
630 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
631 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
633 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
634 if(numberOfResults
> 0) { // no point checking again
635 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
638 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
642 * @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)
645 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
646 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
648 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
649 if(numberOfResults
> 0) { // no point checking again
650 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
653 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
657 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
660 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
661 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
662 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
665 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
668 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
669 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
670 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
678 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
681 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
682 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
683 return t
.getHomotypicSynonymsByHomotypicGroup();
687 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
690 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
691 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
692 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
693 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
694 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
695 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
697 return heterotypicSynonymyGroups
;
701 public List
<UuidAndTitleCache
<IdentifiableEntity
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
703 List
<UuidAndTitleCache
<IdentifiableEntity
>> results
= new ArrayList
<UuidAndTitleCache
<IdentifiableEntity
>>();
706 if (configurator
.isDoSynonyms() || configurator
.isDoTaxa() || configurator
.isDoNamesWithoutTaxa()){
707 results
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.isDoNamesWithoutTaxa(), configurator
.isDoMisappliedNames(),configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
709 if (configurator
.isDoTaxaByCommonNames()) {
711 if(configurator
.getPageSize() == null ){
712 List
<UuidAndTitleCache
<IdentifiableEntity
>> commonNameResults
= dao
.getTaxaByCommonNameForEditor(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
713 if(commonNameResults
!= null){
714 results
.addAll(commonNameResults
);
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
>();
855 List
<Media
> nonImageGalleryImages
= new ArrayList
<Media
>();
857 if (limitToGalleries
== null) {
858 limitToGalleries
= false;
861 // --- resolve related taxa
862 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
863 logger
.trace("listMedia() - resolve related taxa");
864 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
867 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
869 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
870 logger
.trace("listMedia() - includeTaxonDescriptions");
871 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
872 // --- TaxonDescriptions
873 for (Taxon t
: taxa
) {
874 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
876 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
877 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
878 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
879 for (Media media
: element
.getMedia()) {
880 if(taxonDescription
.isImageGallery()){
881 taxonMedia
.add(media
);
884 nonImageGalleryImages
.add(media
);
890 //put images from image gallery first (#3242)
891 taxonMedia
.addAll(nonImageGalleryImages
);
895 if(includeOccurrences
!= null && includeOccurrences
) {
896 logger
.trace("listMedia() - includeOccurrences");
897 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
899 for (Taxon t
: taxa
) {
900 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
902 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
904 // direct media removed from specimen #3597
905 // taxonMedia.addAll(occurrence.getMedia());
907 // SpecimenDescriptions
908 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
909 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
910 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
911 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
912 for (DescriptionElementBase element
: elements
) {
913 for (Media media
: element
.getMedia()) {
914 taxonMedia
.add(media
);
921 //TODO why may collections have media attached? #
922 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
923 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
924 if (derivedUnit
.getCollection() != null){
925 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
929 // pherograms & gelPhotos
930 if (occurrence
.isInstanceOf(DnaSample
.class)) {
931 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
932 Set
<Sequence
> sequences
= dnaSample
.getSequences();
933 //we do show only those gelPhotos which lead to a consensus sequence
934 for (Sequence sequence
: sequences
) {
935 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
936 for (SingleRead singleRead
: sequence
.getSingleReads()){
937 Amplification amplification
= singleRead
.getAmplification();
938 dnaRelatedMedia
.add(amplification
.getGelPhoto());
939 dnaRelatedMedia
.add(singleRead
.getPherogram());
940 dnaRelatedMedia
.remove(null);
942 taxonMedia
.addAll(dnaRelatedMedia
);
949 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
950 logger
.trace("listMedia() - includeTaxonNameDescriptions");
951 // --- TaxonNameDescription
952 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
953 for (Taxon t
: taxa
) {
954 nameDescriptions
.addAll(t
.getName().getDescriptions());
956 for(TaxonNameDescription nameDescription
: nameDescriptions
){
957 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
958 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
959 for (DescriptionElementBase element
: elements
) {
960 for (Media media
: element
.getMedia()) {
961 taxonMedia
.add(media
);
969 logger
.trace("listMedia() - initialize");
970 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
972 logger
.trace("listMedia() - END");
978 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
981 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
982 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
986 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
989 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
990 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
994 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
997 public int countAllRelationships() {
998 return this.dao
.countAllRelationships();
1005 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
1008 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
1009 return this.dao
.findIdenticalTaxonNames(propertyPath
);
1014 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1017 public DeleteResult
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
1018 DeleteResult result
= new DeleteResult();
1019 if (config
== null){
1020 config
= new TaxonDeletionConfigurator();
1023 List
<String
> referencedObjects
= isDeletable(taxon
, config
);
1025 if (referencedObjects
.isEmpty()){
1026 // --- DeleteSynonymRelations
1027 if (config
.isDeleteSynonymRelations()){
1028 boolean removeSynonymNameFromHomotypicalGroup
= false;
1029 // use tmp Set to avoid concurrent modification
1030 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1031 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1032 for (SynonymRelationship synRel
: synRelsToDelete
){
1033 Synonym synonym
= synRel
.getSynonym();
1034 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1035 // this will cause hibernate to delete the relationship since
1036 // the SynonymRelationship field on both is annotated with removeOrphan
1037 // so no further explicit deleting of the relationship should be done here
1038 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1040 // --- DeleteSynonymsIfPossible
1041 if (config
.isDeleteSynonymsIfPossible()){
1043 boolean newHomotypicGroupIfNeeded
= true;
1044 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1045 deleteSynonym(synonym
, taxon
, synConfig
);
1047 // relationship will be deleted by hibernate automatically,
1048 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1050 // deleteSynonymRelationships(synonym, taxon);
1055 // --- DeleteTaxonRelationships
1056 if (! config
.isDeleteTaxonRelationships()){
1057 if (taxon
.getTaxonRelations().size() > 0){
1058 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1059 "Remove taxon from all relations to other taxa prior to deletion.";
1060 // throw new ReferencedObjectUndeletableException(message);
1063 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1064 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1065 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1066 if (taxon
.equals(taxRel
.getToTaxon())){
1067 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1071 taxon
.removeTaxonRelation(taxRel
);
1072 /*if (taxFrom.equals(taxon)){
1074 this.deleteTaxon(taxTo, taxConf, classification);
1075 } catch(DataChangeNoRollbackException e){
1076 logger.debug("A related taxon will not be deleted." + e.getMessage());
1080 this.deleteTaxon(taxFrom, taxConf, classification);
1081 } catch(DataChangeNoRollbackException e){
1082 logger.debug("A related taxon will not be deleted." + e.getMessage());
1090 if (config
.isDeleteDescriptions()){
1091 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1092 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1093 for (TaxonDescription desc
: descriptions
){
1094 //TODO use description delete configurator ?
1095 //FIXME check if description is ALWAYS deletable
1096 if (desc
.getDescribedSpecimenOrObservation() != null){
1097 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1098 " which also describes specimens or abservations";
1099 //throw new ReferencedObjectUndeletableException(message);
1101 removeDescriptions
.add(desc
);
1102 descriptionService
.delete(desc
);
1105 for (TaxonDescription desc
: removeDescriptions
){
1106 taxon
.removeDescription(desc
);
1111 /* //check references with only reverse mapping
1112 String message = checkForReferences(taxon);
1113 if (message != null){
1114 //throw new ReferencedObjectUndeletableException(message.toString());
1117 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1118 //if (taxon.getTaxonNodes().size() > 0){
1119 // 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.";
1120 // throw new ReferencedObjectUndeletableException(message);
1123 if (taxon
.getTaxonNodes().size() != 0){
1124 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1125 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1126 TaxonNode node
= null;
1127 boolean deleteChildren
;
1128 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1129 deleteChildren
= true;
1131 deleteChildren
= false;
1133 boolean success
= true;
1134 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1135 while (iterator
.hasNext()){
1136 node
= iterator
.next();
1137 if (node
.getClassification().equals(classification
)){
1143 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1144 nodeService
.delete(node
);
1147 result
.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
1149 } else if (config
.isDeleteInAllClassifications()){
1150 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1151 nodesList
.addAll(taxon
.getTaxonNodes());
1153 for (ITaxonTreeNode treeNode
: nodesList
){
1154 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1155 if(!deleteChildren
){
1156 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1157 //nodesList.addAll(taxonNode.getChildNodes());
1158 for (Object childNode: childNodes){
1159 TaxonNode childNodeCast = (TaxonNode) childNode;
1160 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1164 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1165 deleteTaxon(childNode.getTaxon(), config, classification);
1168 // taxon.removeTaxonNode(taxonNode);
1169 //nodeService.delete(taxonNode);
1172 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1173 for (Object childNode
: childNodes
){
1174 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1175 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1178 //taxon.removeTaxonNode(taxonNode);
1181 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1182 DeleteResult resultNodes
= nodeService
.deleteTaxonNodes(nodesList
, config
);
1183 if (!resultNodes
.isOk()){
1184 result
.addExceptions(resultNodes
.getExceptions());
1185 result
.setStatus(resultNodes
.getStatus());
1190 result
.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
1196 //PolytomousKey TODO
1200 if (config
.isDeleteNameIfPossible()){
1203 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1204 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1205 //check whether taxon will be deleted or not
1206 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1207 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1208 name
.removeTaxonBase(taxon
);
1209 nameService
.saveOrUpdate(name
);
1210 DeleteResult nameResult
= new DeleteResult();
1212 nameResult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1214 if (nameResult
.isError()){
1215 //result.setError();
1216 result
.addRelatedObject(name
);
1217 result
.addExceptions(nameResult
.getExceptions());
1225 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1227 for (TaxonDescription desc: descriptions){
1228 if (config.isDeleteDescriptions()){
1229 //TODO use description delete configurator ?
1230 //FIXME check if description is ALWAYS deletable
1231 taxon.removeDescription(desc);
1232 descriptionService.delete(desc);
1234 if (desc.getDescribedSpecimenOrObservations().size()>0){
1235 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1236 " which also describes specimens or observations";
1237 throw new ReferencedObjectUndeletableException(message);
1242 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1244 UUID uuid
= dao
.delete(taxon
);
1246 }catch(Exception e
){
1247 result
.addException(e
);
1253 result
.addException(new Exception("The Taxon can't be deleted."));
1257 List
<Exception
> exceptions
= new ArrayList
<Exception
>();
1258 for (String message
: referencedObjects
){
1259 ReferencedObjectUndeletableException exception
= new ReferencedObjectUndeletableException(message
);
1260 exceptions
.add(exception
);
1262 result
.addExceptions(exceptions
);
1270 private String
checkForReferences(Taxon taxon
){
1271 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1272 for (CdmBase referencingObject
: referencingObjects
){
1273 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1274 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1275 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";
1281 /* //PolytomousKeyNode
1282 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1283 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1288 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1289 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1294 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1295 String message
= "Taxon can't be deleted as it is used in a determination event";
1301 referencingObjects
= null;
1305 private boolean checkForPolytomousKeys(Taxon taxon
){
1306 boolean result
= false;
1307 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1308 if (!list
.isEmpty()) {
1314 @Transactional(readOnly
= false)
1315 public UUID
delete(Synonym syn
){
1316 UUID result
= syn
.getUuid();
1317 this.deleteSynonym(syn
, null);
1324 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1326 @Transactional(readOnly
= false)
1328 public DeleteResult
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1329 return deleteSynonym(synonym
, null, config
);
1335 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1337 @Transactional(readOnly
= false)
1339 public DeleteResult
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1340 DeleteResult result
= new DeleteResult();
1341 if (synonym
== null){
1346 if (config
== null){
1347 config
= new SynonymDeletionConfigurator();
1349 List
<String
> messages
= isDeletable(synonym
, config
);
1352 if (messages
.isEmpty()){
1353 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1355 //remove synonymRelationship
1356 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1358 taxonSet
.add(taxon
);
1360 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1362 for (Taxon relatedTaxon
: taxonSet
){
1363 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1364 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1366 this.saveOrUpdate(synonym
);
1368 //TODO remove name from homotypical group?
1370 //remove synonym (if necessary)
1373 if (synonym
.getSynonymRelations().isEmpty()){
1374 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1375 synonym
.setName(null);
1376 uuid
= dao
.delete(synonym
);
1378 //remove name if possible (and required)
1379 if (name
!= null && config
.isDeleteNameIfPossible()){
1381 nameService
.delete(name
, config
.getNameDeletionConfig());
1387 result
.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
1393 List
<Exception
> exceptions
= new ArrayList
<Exception
>();
1394 for (String message
:messages
){
1395 exceptions
.add(new ReferencedObjectUndeletableException(message
));
1398 result
.addExceptions(exceptions
);
1407 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1410 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1412 return this.dao
.findIdenticalNamesNew(propertyPath
);
1416 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1419 public String
getPhylumName(TaxonNameBase name
){
1420 return this.dao
.getPhylumName(name
);
1424 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1427 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1428 return dao
.deleteSynonymRelationships(syn
, taxon
);
1432 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1435 public long deleteSynonymRelationships(Synonym syn
) {
1436 return dao
.deleteSynonymRelationships(syn
, null);
1441 * @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)
1444 public List
<SynonymRelationship
> listSynonymRelationships(
1445 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1446 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1447 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1449 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1450 if(numberOfResults
> 0) { // no point checking again
1451 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1457 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1460 public Taxon
findBestMatchingTaxon(String taxonName
) {
1461 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1462 config
.setTaxonNameTitle(taxonName
);
1463 return findBestMatchingTaxon(config
);
1469 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1471 Taxon bestCandidate
= null;
1473 // 1. search for acceptet taxa
1474 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1475 boolean bestCandidateMatchesSecUuid
= false;
1476 boolean bestCandidateIsInClassification
= false;
1477 int countEqualCandidates
= 0;
1478 for(TaxonBase taxonBaseCandidate
: taxonList
){
1479 if(taxonBaseCandidate
instanceof Taxon
){
1480 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1481 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1482 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1484 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1485 bestCandidate
= newCanditate
;
1486 countEqualCandidates
= 1;
1487 bestCandidateMatchesSecUuid
= true;
1491 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1492 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1494 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1495 bestCandidate
= newCanditate
;
1496 countEqualCandidates
= 1;
1497 bestCandidateIsInClassification
= true;
1500 if (bestCandidate
== null){
1501 bestCandidate
= newCanditate
;
1502 countEqualCandidates
= 1;
1506 }else{ //not Taxon.class
1509 countEqualCandidates
++;
1512 if (bestCandidate
!= null){
1513 if(countEqualCandidates
> 1){
1514 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1515 return bestCandidate
;
1517 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1518 return bestCandidate
;
1523 // 2. search for synonyms
1524 if (config
.isIncludeSynonyms()){
1525 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1526 for(TaxonBase taxonBase
: synonymList
){
1527 if(taxonBase
instanceof Synonym
){
1528 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1529 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1530 if(!acceptetdCandidates
.isEmpty()){
1531 bestCandidate
= acceptetdCandidates
.iterator().next();
1532 if(acceptetdCandidates
.size() == 1){
1533 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1534 return bestCandidate
;
1536 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1537 return bestCandidate
;
1539 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1545 } catch (Exception e
){
1547 e
.printStackTrace();
1550 return bestCandidate
;
1553 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1554 UUID configClassificationUuid
= config
.getClassificationUuid();
1555 if (configClassificationUuid
== null){
1558 for (TaxonNode node
: taxon
.getTaxonNodes()){
1559 UUID classUuid
= node
.getClassification().getUuid();
1560 if (configClassificationUuid
.equals(classUuid
)){
1567 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1568 UUID configSecUuid
= config
.getSecUuid();
1569 if (configSecUuid
== null){
1572 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1573 return configSecUuid
.equals(taxonSecUuid
);
1577 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1580 public Synonym
findBestMatchingSynonym(String taxonName
) {
1581 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1582 if(! synonymList
.isEmpty()){
1583 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1584 if(synonymList
.size() == 1){
1585 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1588 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1597 * @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)
1600 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1601 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1603 Synonym synonym
= oldSynonymRelation
.getSynonym();
1604 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1605 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1606 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1607 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1608 //set default relationship type
1609 if (newSynonymRelationshipType
== null){
1610 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1612 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1614 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1615 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1616 boolean isSingleInGroup
= !(hgSize
> 1);
1618 if (! isSingleInGroup
){
1619 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1620 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1621 if (isHomotypicToAccepted
){
1622 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.";
1623 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1624 message
= String
.format(message
, homotypicRelatives
);
1625 throw new HomotypicalGroupChangeException(message
);
1627 if (! moveHomotypicGroup
){
1628 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.";
1629 throw new HomotypicalGroupChangeException(message
);
1632 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1634 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1636 SynonymRelationship result
= null;
1637 //move all synonyms to new taxon
1638 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1639 for (Synonym syn
: homotypicSynonyms
){
1640 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1641 for (SynonymRelationship synRelation
: synRelations
){
1642 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1643 Reference
<?
> newReference
= reference
;
1644 if (newReference
== null && keepReference
){
1645 newReference
= synRelation
.getCitation();
1647 String newRefDetail
= referenceDetail
;
1648 if (newRefDetail
== null && keepReference
){
1649 newRefDetail
= synRelation
.getCitationMicroReference();
1651 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1652 fromTaxon
.removeSynonymRelation(synRelation
, false);
1654 //change homotypic group of synonym if relType is 'homotypic'
1655 // if (newRelTypeIsHomotypic){
1656 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1659 if (synRelation
.equals(oldSynonymRelation
)){
1660 result
= newSynRelation
;
1666 saveOrUpdate(newTaxon
);
1667 //Assert that there is a result
1668 if (result
== null){
1669 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1670 throw new IllegalStateException(message
);
1676 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1679 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1680 return dao
.getUuidAndTitleCacheTaxon();
1684 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1687 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1688 return dao
.getUuidAndTitleCacheSynonym();
1692 * @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)
1695 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1696 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1697 Classification classification
, List
<Language
> languages
,
1698 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1701 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1703 // --- execute search
1704 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1706 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1707 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1709 // --- initialize taxa, thighlight matches ....
1710 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1711 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1712 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1714 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1715 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1719 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTerm
> statusFilter
,
1720 Classification classification
,
1721 Integer pageSize
, Integer pageNumber
,
1722 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1724 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1726 // --- execute search
1727 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1729 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1730 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1732 // --- initialize taxa, thighlight matches ....
1733 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1734 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1735 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1737 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1738 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1743 * @param queryString
1744 * @param classification
1746 * @param highlightFragments
1747 * @param sortFields TODO
1748 * @param directorySelectClass
1751 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1752 boolean highlightFragments
, SortField
[] sortFields
) {
1753 BooleanQuery finalQuery
= new BooleanQuery();
1754 BooleanQuery textQuery
= new BooleanQuery();
1756 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1757 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1759 if(sortFields
== null){
1760 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1762 luceneSearch
.setSortFields(sortFields
);
1764 // ---- search criteria
1765 luceneSearch
.setCdmTypRestriction(clazz
);
1767 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1768 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1770 finalQuery
.add(textQuery
, Occur
.MUST
);
1772 if(classification
!= null){
1773 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1775 luceneSearch
.setQuery(finalQuery
);
1777 if(highlightFragments
){
1778 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1780 return luceneSearch
;
1784 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1785 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1786 * drawback of requiring to do the join an indexing time.
1787 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1789 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1791 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1792 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1794 * @param queryString
1795 * @param classification
1797 * @param highlightFragments
1798 * @param sortFields TODO
1801 * @throws IOException
1803 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1804 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1807 String queryTermField
;
1808 String toField
= "id"; // TaxonBase.uuid
1810 if(edge
.isBidirectional()){
1811 throw new RuntimeException("Bidirectional joining not supported!");
1814 fromField
= "relatedFrom.id";
1815 queryTermField
= "relatedFrom.titleCache";
1816 } else if(edge
.isInvers()) {
1817 fromField
= "relatedTo.id";
1818 queryTermField
= "relatedTo.titleCache";
1820 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1823 BooleanQuery finalQuery
= new BooleanQuery();
1825 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1826 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1828 BooleanQuery joinFromQuery
= new BooleanQuery();
1829 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1830 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1831 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1833 if(sortFields
== null){
1834 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1836 luceneSearch
.setSortFields(sortFields
);
1838 finalQuery
.add(joinQuery
, Occur
.MUST
);
1840 if(classification
!= null){
1841 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1843 luceneSearch
.setQuery(finalQuery
);
1845 if(highlightFragments
){
1846 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1848 return luceneSearch
;
1855 * @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)
1858 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1859 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1860 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTerm
> distributionStatus
, List
<Language
> languages
,
1861 boolean highlightFragments
, Integer pageSize
,
1862 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1863 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1865 // FIXME: allow taxonomic ordering
1866 // 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";
1867 // this require building a special sort column by a special classBridge
1868 if(highlightFragments
){
1869 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1870 "currently not fully supported by this method and thus " +
1871 "may not work with common names and misapplied names.");
1874 // convert sets to lists
1875 List
<NamedArea
> namedAreaList
= null;
1876 List
<PresenceAbsenceTerm
>distributionStatusList
= null;
1877 if(namedAreas
!= null){
1878 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1879 namedAreaList
.addAll(namedAreas
);
1881 if(distributionStatus
!= null){
1882 distributionStatusList
= new ArrayList
<PresenceAbsenceTerm
>(distributionStatus
.size());
1883 distributionStatusList
.addAll(distributionStatus
);
1886 // set default if parameter is null
1887 if(searchModes
== null){
1888 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1891 // set sort order and thus override any sort orders which may have been
1892 // defindes by prepare*Search methods
1893 if(orderHints
== null){
1894 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1896 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1898 for(OrderHint oh
: orderHints
){
1899 sortFields
[i
++] = oh
.toSortField();
1901 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1902 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1905 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1907 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1908 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1911 ======== filtering by distribution , HOWTO ========
1913 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1914 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1915 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1916 which will be put into a FilteredQuersy in the end ?
1919 3. how does it work in spatial?
1921 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1922 - http://www.infoq.com/articles/LuceneSpatialSupport
1923 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1924 ------------------------------------------------------------------------
1927 A) use a separate distribution filter per index sub-query/search:
1928 - byTaxonSyonym (query TaxaonBase):
1929 use a join area filter (Distribution -> TaxonBase)
1930 - byCommonName (query DescriptionElementBase): use an area filter on
1931 DescriptionElementBase !!! PROBLEM !!!
1932 This cannot work since the distributions are different entities than the
1933 common names and thus these are different lucene documents.
1934 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1935 use a join area filter (Distribution -> TaxonBase)
1937 B) use a common distribution filter for all index sub-query/searches:
1938 - use a common join area filter (Distribution -> TaxonBase)
1939 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1940 PROBLEM in this case: we are losing the fragment highlighting for the
1941 common names, since the returned documents are always TaxonBases
1944 /* The QueryFactory for creating filter queries on Distributions should
1945 * The query factory used for the common names query cannot be reused
1946 * for this case, since we want to only record the text fields which are
1947 * actually used in the primary query
1949 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1951 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1954 // search for taxa or synonyms
1955 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1956 Class taxonBaseSubclass
= TaxonBase
.class;
1957 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1958 taxonBaseSubclass
= Taxon
.class;
1959 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1960 taxonBaseSubclass
= Synonym
.class;
1962 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1963 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1964 /* A) does not work!!!!
1965 if(addDistributionFilter){
1966 // in this case we need a filter which uses a join query
1967 // to get the TaxonBase documents for the DescriptionElementBase documents
1968 // which are matching the areas in question
1969 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1971 distributionStatusList,
1972 distributionFilterQueryFactory
1974 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1977 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1978 // add additional area filter for synonyms
1979 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1980 String toField
= "accTaxon.id"; // id in TaxonBase index
1982 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1984 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1985 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1990 // search by CommonTaxonName
1991 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1993 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1994 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1995 "inDescription.taxon.id",
1997 QueryFactory
.addTypeRestriction(
1998 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1999 , CommonTaxonName
.class
2001 CommonTaxonName
.class);
2002 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
2003 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2004 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
2005 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
2006 byCommonNameSearch
.setSortFields(sortFields
);
2007 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2009 luceneSearches
.add(byCommonNameSearch
);
2011 /* A) does not work!!!!
2013 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
2014 queryString, classification, null, languages, highlightFragments)
2016 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2017 if(addDistributionFilter){
2018 // in this case we are able to use DescriptionElementBase documents
2019 // which are matching the areas in question directly
2020 BooleanQuery byDistributionQuery = createByDistributionQuery(
2022 distributionStatusList,
2023 distributionFilterQueryFactory
2025 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2029 // search by misapplied names
2030 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
2032 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2033 // which allows doing query time joins
2034 // finds the misapplied name (Taxon B) which is an misapplication for
2035 // a related Taxon A.
2037 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
2038 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
2039 queryString
, classification
, languages
, highlightFragments
, sortFields
));
2040 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2042 if(addDistributionFilter
){
2043 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2046 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2047 * Maybe this is a but in java itself java.
2049 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2052 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2054 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2055 * will execute as expected:
2057 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2058 * String toField = "relation." + misappliedNameForUuid +".to.id";
2060 * Comparing both strings by the String.equals method returns true, so both String are identical.
2062 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2063 * 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)
2064 * The bug is persistent after a reboot of the development computer.
2066 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2067 // String toField = "relation." + misappliedNameForUuid +".to.id";
2068 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2069 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2070 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2072 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2073 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2074 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2076 // debug code for bug described above
2077 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2078 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2080 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2084 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2085 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2088 if(addDistributionFilter
){
2091 // in this case we need a filter which uses a join query
2092 // to get the TaxonBase documents for the DescriptionElementBase documents
2093 // which are matching the areas in question
2095 // for toTaxa, doByCommonName
2096 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2098 distributionStatusList
,
2099 distributionFilterQueryFactory
2101 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2104 if (addDistributionFilter
){
2105 multiSearch
.setFilter(multiIndexByAreaFilter
);
2109 // --- execute search
2110 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2112 // --- initialize taxa, highlight matches ....
2113 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2116 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2117 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2119 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2120 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2124 * @param namedAreaList at least one area must be in the list
2125 * @param distributionStatusList optional
2127 * @throws IOException
2129 protected Query
createByDistributionJoinQuery(
2130 List
<NamedArea
> namedAreaList
,
2131 List
<PresenceAbsenceTerm
> distributionStatusList
,
2132 QueryFactory queryFactory
2133 ) throws IOException
{
2135 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2136 String toField
= "id"; // id in TaxonBase index
2138 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2140 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2142 return taxonAreaJoinQuery
;
2146 * @param namedAreaList
2147 * @param distributionStatusList
2148 * @param queryFactory
2151 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2152 List
<PresenceAbsenceTerm
> distributionStatusList
, QueryFactory queryFactory
) {
2153 BooleanQuery areaQuery
= new BooleanQuery();
2154 // area field from Distribution
2155 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2157 // status field from Distribution
2158 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2159 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2162 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2167 * This method has been primarily created for testing the area join query but might
2168 * also be useful in other situations
2170 * @param namedAreaList
2171 * @param distributionStatusList
2172 * @param classification
2173 * @param highlightFragments
2175 * @throws IOException
2177 protected LuceneSearch
prepareByDistributionSearch(
2178 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTerm
> distributionStatusList
,
2179 Classification classification
) throws IOException
{
2181 BooleanQuery finalQuery
= new BooleanQuery();
2183 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2185 // FIXME is this query factory using the wrong type?
2186 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2188 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2189 luceneSearch
.setSortFields(sortFields
);
2192 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2194 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2196 if(classification
!= null){
2197 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2200 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2201 luceneSearch
.setQuery(finalQuery
);
2203 return luceneSearch
;
2209 * @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)
2212 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2213 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2214 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2215 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2218 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2220 // --- execute search
2221 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2223 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2224 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2226 // --- initialize taxa, highlight matches ....
2227 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2228 @SuppressWarnings("rawtypes")
2229 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2230 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2232 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2233 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2239 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2240 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2241 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2243 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2244 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2246 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2248 // --- execute search
2249 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2251 // --- initialize taxa, highlight matches ....
2252 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2254 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2255 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2256 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2258 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2259 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2261 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2262 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2269 * @param queryString
2270 * @param classification
2273 * @param highlightFragments
2274 * @param directorySelectClass
2277 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2278 String queryString
, Classification classification
, List
<Feature
> features
,
2279 List
<Language
> languages
, boolean highlightFragments
) {
2281 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2282 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2284 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2286 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2287 languages
, descriptionElementQueryFactory
);
2289 luceneSearch
.setSortFields(sortFields
);
2290 luceneSearch
.setCdmTypRestriction(clazz
);
2291 luceneSearch
.setQuery(finalQuery
);
2292 if(highlightFragments
){
2293 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2296 return luceneSearch
;
2300 * @param queryString
2301 * @param classification
2304 * @param descriptionElementQueryFactory
2307 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2308 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2309 BooleanQuery finalQuery
= new BooleanQuery();
2310 BooleanQuery textQuery
= new BooleanQuery();
2311 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2315 if(languages
== null || languages
.size() == 0){
2316 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2318 nameQuery
= new BooleanQuery();
2319 BooleanQuery languageSubQuery
= new BooleanQuery();
2320 for(Language lang
: languages
){
2321 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2323 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2324 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2326 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2329 // text field from TextData
2330 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2332 // --- TermBase fields - by representation ----
2333 // state field from CategoricalData
2334 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2336 // state field from CategoricalData
2337 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2339 // area field from Distribution
2340 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2342 // status field from Distribution
2343 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2345 finalQuery
.add(textQuery
, Occur
.MUST
);
2346 // --- classification ----
2348 if(classification
!= null){
2349 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2352 // --- IdentifieableEntity fields - by uuid
2353 if(features
!= null && features
.size() > 0 ){
2354 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2357 // the description must be associated with a taxon
2358 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2360 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2365 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2366 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2367 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2369 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2370 * or {@link MultilanguageTextFieldBridge }
2371 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2372 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2373 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2375 * TODO move to utiliy class !!!!!!!!
2377 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2379 if(stringBuilder
== null){
2380 stringBuilder
= new StringBuilder();
2382 if(languages
== null || languages
.size() == 0){
2383 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2385 for(Language lang
: languages
){
2386 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2389 return stringBuilder
;
2393 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2394 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2395 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2397 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2400 UUID nameUuid
= taxon
.getName().getUuid();
2401 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2402 String epithetOfTaxon
= null;
2403 String infragenericEpithetOfTaxon
= null;
2404 String infraspecificEpithetOfTaxon
= null;
2405 if (taxonName
.isSpecies()){
2406 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2407 } else if (taxonName
.isInfraGeneric()){
2408 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2409 } else if (taxonName
.isInfraSpecific()){
2410 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2412 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2413 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2414 List
<String
> taxonNames
= new ArrayList
<String
>();
2416 for (TaxonNode node
: nodes
){
2417 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2418 // List<String> synonymsEpithet = new ArrayList<String>();
2420 if (node
.getClassification().equals(classification
)){
2421 if (!node
.isTopmostNode()){
2422 TaxonNode parent
= node
.getParent();
2423 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2424 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2425 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2426 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2427 Rank rankOfTaxon
= taxonName
.getRank();
2430 //create inferred synonyms for species, subspecies
2431 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2433 Synonym inferredEpithet
= null;
2434 Synonym inferredGenus
= null;
2435 Synonym potentialCombination
= null;
2437 List
<String
> propertyPaths
= new ArrayList
<String
>();
2438 propertyPaths
.add("synonym");
2439 propertyPaths
.add("synonym.name");
2440 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2441 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2443 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2444 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2446 List
<TaxonRelationship
> taxonRelListParent
= null;
2447 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2448 if (doWithMisappliedNames
){
2449 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2450 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2454 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2457 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2458 Synonym syn
= synonymRelationOfParent
.getSynonym();
2460 inferredEpithet
= createInferredEpithets(taxon
,
2461 zooHashMap
, taxonName
, epithetOfTaxon
,
2462 infragenericEpithetOfTaxon
,
2463 infraspecificEpithetOfTaxon
,
2464 taxonNames
, parentName
,
2468 inferredSynonyms
.add(inferredEpithet
);
2469 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2470 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2473 if (doWithMisappliedNames
){
2475 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2476 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2478 inferredEpithet
= createInferredEpithets(taxon
,
2479 zooHashMap
, taxonName
, epithetOfTaxon
,
2480 infragenericEpithetOfTaxon
,
2481 infraspecificEpithetOfTaxon
,
2482 taxonNames
, parentName
,
2485 inferredSynonyms
.add(inferredEpithet
);
2486 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2487 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2491 if (!taxonNames
.isEmpty()){
2492 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2493 ZoologicalName name
;
2494 if (!synNotInCDM
.isEmpty()){
2495 inferredSynonymsToBeRemoved
.clear();
2497 for (Synonym syn
:inferredSynonyms
){
2498 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2499 if (!synNotInCDM
.contains(name
.getNameCache())){
2500 inferredSynonymsToBeRemoved
.add(syn
);
2504 // Remove identified Synonyms from inferredSynonyms
2505 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2506 inferredSynonyms
.remove(synonym
);
2511 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2514 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2515 TaxonNameBase synName
;
2516 ZoologicalName inferredSynName
;
2518 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2519 inferredGenus
= createInferredGenus(taxon
,
2520 zooHashMap
, taxonName
, epithetOfTaxon
,
2521 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2523 inferredSynonyms
.add(inferredGenus
);
2524 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2525 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2530 if (doWithMisappliedNames
){
2532 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2533 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2534 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2536 inferredSynonyms
.add(inferredGenus
);
2537 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2538 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2543 if (!taxonNames
.isEmpty()){
2544 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2545 ZoologicalName name
;
2546 if (!synNotInCDM
.isEmpty()){
2547 inferredSynonymsToBeRemoved
.clear();
2549 for (Synonym syn
:inferredSynonyms
){
2550 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2551 if (!synNotInCDM
.contains(name
.getNameCache())){
2552 inferredSynonymsToBeRemoved
.add(syn
);
2556 // Remove identified Synonyms from inferredSynonyms
2557 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2558 inferredSynonyms
.remove(synonym
);
2563 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2565 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2566 ZoologicalName inferredSynName
;
2567 //for all synonyms of the parent...
2568 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2569 TaxonNameBase synName
;
2570 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2571 synName
= synParent
.getName();
2573 HibernateProxyHelper
.deproxy(synParent
);
2575 // Set the sourceReference
2576 sourceReference
= synParent
.getSec();
2578 // Determine the idInSource
2579 String idInSourceParent
= getIdInSource(synParent
);
2581 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2582 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2583 String synParentInfragenericName
= null;
2584 String synParentSpecificEpithet
= null;
2586 if (parentSynZooName
.isInfraGeneric()){
2587 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2589 if (parentSynZooName
.isSpecies()){
2590 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2593 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2594 synonymsGenus.put(synGenusName, idInSource);
2597 //for all synonyms of the taxon
2599 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2601 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2602 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2603 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2605 synParentInfragenericName
,
2606 synParentSpecificEpithet
, syn
, zooHashMap
);
2608 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2609 inferredSynonyms
.add(potentialCombination
);
2610 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2611 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2618 if (doWithMisappliedNames
){
2620 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2622 TaxonNameBase misappliedParentName
;
2624 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2625 misappliedParentName
= misappliedParent
.getName();
2627 HibernateProxyHelper
.deproxy(misappliedParent
);
2629 // Set the sourceReference
2630 sourceReference
= misappliedParent
.getSec();
2632 // Determine the idInSource
2633 String idInSourceParent
= getIdInSource(misappliedParent
);
2635 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2636 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2637 String synParentInfragenericName
= null;
2638 String synParentSpecificEpithet
= null;
2640 if (parentSynZooName
.isInfraGeneric()){
2641 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2643 if (parentSynZooName
.isSpecies()){
2644 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2648 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2649 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2650 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2651 potentialCombination
= createPotentialCombination(
2652 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2654 synParentInfragenericName
,
2655 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2658 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2659 inferredSynonyms
.add(potentialCombination
);
2660 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2661 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2666 if (!taxonNames
.isEmpty()){
2667 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2668 ZoologicalName name
;
2669 if (!synNotInCDM
.isEmpty()){
2670 inferredSynonymsToBeRemoved
.clear();
2671 for (Synonym syn
:inferredSynonyms
){
2673 name
= (ZoologicalName
) syn
.getName();
2674 }catch (ClassCastException e
){
2675 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2677 if (!synNotInCDM
.contains(name
.getNameCache())){
2678 inferredSynonymsToBeRemoved
.add(syn
);
2681 // Remove identified Synonyms from inferredSynonyms
2682 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2683 inferredSynonyms
.remove(synonym
);
2689 logger
.info("The synonymrelationship type is not defined.");
2690 return inferredSynonyms
;
2697 return inferredSynonyms
;
2700 private Synonym
createPotentialCombination(String idInSourceParent
,
2701 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2702 String synParentInfragenericName
, String synParentSpecificEpithet
,
2703 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2704 Synonym potentialCombination
;
2705 Reference sourceReference
;
2706 ZoologicalName inferredSynName
;
2707 HibernateProxyHelper
.deproxy(syn
);
2709 // Set sourceReference
2710 sourceReference
= syn
.getSec();
2711 if (sourceReference
== null){
2712 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2714 if (!parentSynZooName
.getTaxa().isEmpty()){
2715 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2717 sourceReference
= taxon
.getSec();
2720 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2722 String synTaxonInfraSpecificName
= null;
2724 if (parentSynZooName
.isSpecies()){
2725 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2728 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2729 synonymsEpithet.add(epithetName);
2732 //create potential combinations...
2733 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2735 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2736 if (zooSynName
.isSpecies()){
2737 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2738 if (parentSynZooName
.isInfraGeneric()){
2739 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2742 if (zooSynName
.isInfraSpecific()){
2743 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2744 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2746 if (parentSynZooName
.isInfraGeneric()){
2747 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2751 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2753 // Set the sourceReference
2754 potentialCombination
.setSec(sourceReference
);
2757 // Determine the idInSource
2758 String idInSourceSyn
= getIdInSource(syn
);
2760 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2761 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2762 inferredSynName
.addSource(originalSource
);
2763 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2764 potentialCombination
.addSource(originalSource
);
2767 return potentialCombination
;
2770 private Synonym
createInferredGenus(Taxon taxon
,
2771 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2772 String epithetOfTaxon
, String genusOfTaxon
,
2773 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2776 Synonym inferredGenus
;
2777 TaxonNameBase synName
;
2778 ZoologicalName inferredSynName
;
2779 synName
=syn
.getName();
2780 HibernateProxyHelper
.deproxy(syn
);
2782 // Determine the idInSource
2783 String idInSourceSyn
= getIdInSource(syn
);
2784 String idInSourceTaxon
= getIdInSource(taxon
);
2785 // Determine the sourceReference
2786 Reference sourceReference
= syn
.getSec();
2788 //logger.warn(sourceReference.getTitleCache());
2790 synName
= syn
.getName();
2791 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2792 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2793 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2794 synonymsEpithet.add(synSpeciesEpithetName);
2797 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2798 //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...
2801 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2802 if (zooParentName
.isInfraGeneric()){
2803 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2806 if (taxonName
.isSpecies()){
2807 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2809 if (taxonName
.isInfraSpecific()){
2810 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2811 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2815 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2817 // Set the sourceReference
2818 inferredGenus
.setSec(sourceReference
);
2820 // Add the original source
2821 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2822 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2823 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2824 inferredGenus
.addSource(originalSource
);
2826 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2827 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2828 inferredSynName
.addSource(originalSource
);
2829 originalSource
= null;
2832 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2833 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2834 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2835 inferredGenus
.addSource(originalSource
);
2837 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2838 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2839 inferredSynName
.addSource(originalSource
);
2840 originalSource
= null;
2843 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2845 return inferredGenus
;
2848 private Synonym
createInferredEpithets(Taxon taxon
,
2849 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2850 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2851 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2852 TaxonNameBase parentName
, TaxonBase syn
) {
2854 Synonym inferredEpithet
;
2855 TaxonNameBase
<?
,?
> synName
;
2856 ZoologicalName inferredSynName
;
2857 HibernateProxyHelper
.deproxy(syn
);
2859 // Determine the idInSource
2860 String idInSourceSyn
= getIdInSource(syn
);
2861 String idInSourceTaxon
= getIdInSource(taxon
);
2862 // Determine the sourceReference
2863 Reference
<?
> sourceReference
= syn
.getSec();
2865 if (sourceReference
== null){
2866 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2867 sourceReference
= taxon
.getSec();
2870 synName
= syn
.getName();
2871 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2872 String synGenusName
= zooSynName
.getGenusOrUninomial();
2873 String synInfraGenericEpithet
= null;
2874 String synSpecificEpithet
= null;
2876 if (zooSynName
.getInfraGenericEpithet() != null){
2877 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2880 if (zooSynName
.isInfraSpecific()){
2881 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2884 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2885 synonymsGenus.put(synGenusName, idInSource);
2888 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2890 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2891 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2892 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2894 inferredSynName
.setGenusOrUninomial(synGenusName
);
2896 if (parentName
.isInfraGeneric()){
2897 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2899 if (taxonName
.isSpecies()){
2900 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2901 }else if (taxonName
.isInfraSpecific()){
2902 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2903 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2906 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2908 // Set the sourceReference
2909 inferredEpithet
.setSec(sourceReference
);
2911 /* Add the original source
2912 if (idInSource != null) {
2913 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2916 Reference citation = getCitation(syn);
2917 if (citation != null) {
2918 originalSource.setCitation(citation);
2919 inferredEpithet.addSource(originalSource);
2922 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2925 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2926 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2928 inferredEpithet
.addSource(originalSource
);
2930 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2931 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2933 inferredSynName
.addSource(originalSource
);
2937 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2939 return inferredEpithet
;
2943 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2944 * Very likely only useful for createInferredSynonyms().
2949 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2950 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2951 if (taxonName
== null) {
2952 taxonName
= zooHashMap
.get(uuid
);
2958 * Returns the idInSource for a given Synonym.
2961 private String
getIdInSource(TaxonBase taxonBase
) {
2962 String idInSource
= null;
2963 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2964 if (sources
.size() == 1) {
2965 IdentifiableSource source
= sources
.iterator().next();
2966 if (source
!= null) {
2967 idInSource
= source
.getIdInSource();
2969 } else if (sources
.size() > 1) {
2972 for (IdentifiableSource source
: sources
) {
2973 idInSource
+= source
.getIdInSource();
2974 if (count
< sources
.size()) {
2979 } else if (sources
.size() == 0){
2980 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2989 * Returns the citation for a given Synonym.
2992 private Reference
getCitation(Synonym syn
) {
2993 Reference citation
= null;
2994 Set
<IdentifiableSource
> sources
= syn
.getSources();
2995 if (sources
.size() == 1) {
2996 IdentifiableSource source
= sources
.iterator().next();
2997 if (source
!= null) {
2998 citation
= source
.getCitation();
3000 } else if (sources
.size() > 1) {
3001 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
3008 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
3009 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
3011 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
3012 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
3013 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
3015 return inferredSynonyms
;
3019 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
3021 // TODO quickly implemented, create according dao !!!!
3022 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
3023 Set
<Classification
> classifications
= new HashSet
<Classification
>();
3024 List
<Classification
> list
= new ArrayList
<Classification
>();
3026 if (taxonBase
== null) {
3030 taxonBase
= load(taxonBase
.getUuid());
3032 if (taxonBase
instanceof Taxon
) {
3033 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
3035 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
3036 nodes
.addAll(taxon
.getTaxonNodes());
3039 for (TaxonNode node
: nodes
) {
3040 classifications
.add(node
.getClassification());
3042 list
.addAll(classifications
);
3047 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3048 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3049 // Create new synonym using concept name
3050 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3051 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3053 // Remove concept relation from taxon
3054 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3059 // Create a new synonym for the taxon
3060 SynonymRelationship synonymRelationship
;
3061 if (synonymRelationshipType
!= null
3062 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3063 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3065 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3068 this.saveOrUpdate(toTaxon
);
3069 //TODO: configurator and classification
3070 TaxonDeletionConfigurator config
= new TaxonDeletionConfigurator();
3071 config
.setDeleteNameIfPossible(false);
3072 this.deleteTaxon(fromTaxon
, config
, null);
3073 return synonymRelationship
.getSynonym();
3077 public List
<String
> isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3078 List
<String
> result
= new ArrayList
<String
>();
3079 Set
<CdmBase
> references
= commonService
.getReferencingObjectsForDeletion(taxonBase
);
3080 if (taxonBase
instanceof Taxon
){
3081 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3082 result
= isDeletableForTaxon(references
, taxonConfig
);
3084 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3085 result
= isDeletableForSynonym(references
, synonymConfig
);
3090 private List
<String
> isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3092 List
<String
> result
= new ArrayList
<String
>();
3093 for (CdmBase ref
: references
){
3094 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3095 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3096 result
.add(message
);
3102 private List
<String
> isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3104 List
<String
> result
= new ArrayList
<String
>();
3105 for (CdmBase ref
: references
){
3106 if (!(ref
instanceof TaxonNameBase
)){
3107 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3108 message
= "The Taxon can't be deleted as long as it has synonyms.";
3109 result
.add(message
);
3111 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3112 message
= "The Taxon can't be deleted as long as it has factual data.";
3113 result
.add(message
);
3116 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3117 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3118 result
.add(message
);
3120 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3121 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3122 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3123 result
.add(message
);
3125 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3126 result
.add(message
);
3129 if (ref
instanceof PolytomousKeyNode
){
3130 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3131 result
.add(message
);
3134 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3135 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3136 result
.add(message
);
3141 /* //PolytomousKeyNode
3142 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3143 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3148 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3149 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3150 result
.add(message
);
3154 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3155 message
= "Taxon can't be deleted as it is used in a determination event";
3156 result
.add(message
);
3167 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3168 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3170 //preliminary implementation
3172 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3173 TaxonBase taxonBase
= find(taxonUuid
);
3174 if (taxonBase
== null){
3175 return new IncludedTaxaDTO();
3176 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3177 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3179 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3180 //TODO partial synonyms ??
3181 //TODO synonyms in general
3182 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3183 taxa
.addAll(syn
.getAcceptedTaxa());
3185 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3188 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3190 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3191 related
= makeRelatedIncluded(related
, result
, config
);
3198 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3200 * @return the set of conceptually related taxa for further use
3203 * @param uncheckedTaxa
3204 * @param existingTaxa
3208 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3211 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3212 for (Taxon taxon
: uncheckedTaxa
){
3213 taxonNodes
.addAll(taxon
.getTaxonNodes());
3216 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3217 if (! config
.onlyCongruent
){
3218 for (TaxonNode node
: taxonNodes
){
3219 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3220 for (TaxonNode child
: childNodes
){
3221 children
.add(child
.getTaxon());
3224 children
.remove(null); // just to be on the save side
3227 Iterator
<Taxon
> it
= children
.iterator();
3228 while(it
.hasNext()){
3229 UUID uuid
= it
.next().getUuid();
3230 if (existingTaxa
.contains(uuid
)){
3233 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3238 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3239 uncheckedAndChildren
.addAll(children
);
3241 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3244 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3249 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3250 * @return the set of these computed taxa
3252 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3253 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3255 for (Taxon taxon
: unchecked
){
3256 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3257 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3259 for (TaxonRelationship fromRel
: fromRelations
){
3260 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3263 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3264 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3265 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3267 result
.add(fromRel
.getToTaxon());
3271 for (TaxonRelationship toRel
: toRelations
){
3272 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3275 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3276 result
.add(toRel
.getFromTaxon());
3281 Iterator
<Taxon
> it
= result
.iterator();
3282 while(it
.hasNext()){
3283 UUID uuid
= it
.next().getUuid();
3284 if (existingTaxa
.contains(uuid
)){
3287 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3293 public List
<TaxonBase
> findTaxaByName(MatchingTaxonConfigurator config
){
3294 List
<TaxonBase
> taxonList
= dao
.getTaxaByName(true, false, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, 0, config
.getPropertyPath());