3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
11 package eu
.etaxonomy
.cdm
.api
.service
;
13 import java
.io
.IOException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.EnumSet
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
22 import java
.util
.UUID
;
24 import javax
.persistence
.EntityNotFoundException
;
26 import org
.apache
.log4j
.Logger
;
27 import org
.apache
.lucene
.index
.CorruptIndexException
;
28 import org
.apache
.lucene
.queryParser
.ParseException
;
29 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
30 import org
.apache
.lucene
.search
.BooleanFilter
;
31 import org
.apache
.lucene
.search
.BooleanQuery
;
32 import org
.apache
.lucene
.search
.DocIdSet
;
33 import org
.apache
.lucene
.search
.Query
;
34 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
35 import org
.apache
.lucene
.search
.SortField
;
36 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
37 import org
.springframework
.stereotype
.Service
;
38 import org
.springframework
.transaction
.annotation
.Transactional
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.DeleteConfiguratorBase
;
41 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
42 import eu
.etaxonomy
.cdm
.api
.service
.config
.IncludedTaxonConfiguration
;
43 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
44 import eu
.etaxonomy
.cdm
.api
.service
.config
.SynonymDeletionConfigurator
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
47 import eu
.etaxonomy
.cdm
.api
.service
.dto
.FindByIdentifierDTO
;
48 import eu
.etaxonomy
.cdm
.api
.service
.dto
.IncludedTaxaDTO
;
49 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
50 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
51 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
52 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
53 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
57 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
58 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
59 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
60 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
61 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
62 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
63 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
64 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
65 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
66 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
67 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
68 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
69 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
70 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
71 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
72 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
73 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
74 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
75 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
76 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
77 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
78 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
79 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
80 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
81 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
82 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
83 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
84 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
85 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
86 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
87 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
88 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
89 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
90 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
91 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
92 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
93 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
94 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
95 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
96 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
97 import eu
.etaxonomy
.cdm
.model
.molecular
.AmplificationResult
;
98 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
99 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
100 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
101 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
102 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
103 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
104 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
105 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
106 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
107 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
108 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
109 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
111 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
112 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
113 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
114 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
115 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
116 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
117 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
118 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
119 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
120 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
121 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
122 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
123 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
124 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
125 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
126 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
127 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
128 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
129 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
130 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
131 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
135 * @author a.kohlbecker
140 @Transactional(readOnly
= true)
141 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
142 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
144 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
146 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
148 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
152 private ITaxonNameDao nameDao
;
155 private INameService nameService
;
158 private ITaxonNodeService nodeService
;
161 private ICdmGenericDao genericDao
;
164 private IDescriptionService descriptionService
;
167 private IOrderedTermVocabularyDao orderedVocabularyDao
;
170 private IOccurrenceDao occurrenceDao
;
173 private IClassificationDao classificationDao
;
176 private AbstractBeanInitializer beanInitializer
;
179 private ILuceneIndexToolProvider luceneIndexToolProvider
;
184 public TaxonServiceImpl(){
185 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
189 * FIXME Candidate for harmonization
190 * rename searchByName ?
193 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
194 return dao
.getTaxaByName(name
, sec
);
198 * FIXME Candidate for harmonization
199 * merge with getRootTaxa(Reference sec, ..., ...)
201 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
204 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
205 if (cdmFetch
== null){
206 cdmFetch
= CdmFetch
.NO_FETCH();
208 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
212 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
213 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
217 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
218 return dao
.getAllRelationships(limit
, start
);
222 * FIXME Candidate for harmonization
223 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
227 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
229 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
230 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
231 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
232 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
233 return taxonRelTypeVocabulary
;
240 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
243 @Transactional(readOnly
= false)
244 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
246 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
247 synonymName
.removeTaxonBase(synonym
);
248 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
249 taxonName
.removeTaxonBase(acceptedTaxon
);
251 synonym
.setName(taxonName
);
252 acceptedTaxon
.setName(synonymName
);
254 // the accepted taxon needs a new uuid because the concept has changed
255 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
256 //acceptedTaxon.setUuid(UUID.randomUUID());
261 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
265 @Transactional(readOnly
= false)
266 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
268 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
269 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
270 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
272 //check synonym is not homotypic
273 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
274 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
275 throw new HomotypicalGroupChangeException(message
);
278 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
280 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
281 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
282 Set
<NameRelationship
> basionymsAndReplacedSynonyms
= synonymHomotypicGroup
.getBasionymAndReplacedSynonymRelations();
284 for (Synonym heteroSynonym
: heteroSynonyms
){
285 if (synonym
.equals(heteroSynonym
)){
286 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
289 //move synonyms in same homotypic group to new accepted taxon
290 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
294 //synonym.getName().removeTaxonBase(synonym);
297 // deleteSynonym(synonym, taxon, false);
300 SynonymDeletionConfigurator config
= new SynonymDeletionConfigurator();
301 config
.setDeleteNameIfPossible(false);
302 this.deleteSynonym(synonym
, acceptedTaxon
, config
);
304 } catch (Exception e
) {
305 logger
.info("Can't delete old synonym from database");
309 return newAcceptedTaxon
;
314 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
316 // Get name from synonym
317 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
319 /* // remove synonym from taxon
320 toTaxon.removeSynonym(synonym);
322 // Create a taxon with synonym name
323 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
325 // Add taxon relation
326 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
328 // since we are swapping names, we have to detach the name from the synonym completely.
329 // Otherwise the synonym will still be in the list of typified names.
330 // synonym.getName().removeTaxonBase(synonym);
331 this.deleteSynonym(synonym
, null);
338 * @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)
340 @Transactional(readOnly
= false)
342 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
343 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
345 TaxonNameBase synonymName
= synonym
.getName();
346 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
350 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
351 newHomotypicalGroup
.addTypifiedName(synonymName
);
353 //remove existing basionym relationships
354 synonymName
.removeBasionyms();
356 //add basionym relationship
357 if (setBasionymRelationIfApplicable
){
358 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
359 for (TaxonNameBase basionym
: basionyms
){
360 synonymName
.addBasionym(basionym
);
364 //set synonym relationship correctly
365 // SynonymRelationship relToTaxon = null;
366 boolean relToTargetTaxonExists
= false;
367 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
368 for (SynonymRelationship rel
: existingRelations
){
369 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
370 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
371 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
372 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
373 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
374 rel
.setType(newRelationType
);
375 //TODO handle citation and microCitation
378 relToTargetTaxonExists
= true;
380 if (removeFromOtherTaxa
){
381 acceptedTaxon
.removeSynonym(synonym
, false);
387 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
388 Taxon acceptedTaxon
= targetTaxon
;
389 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
390 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
391 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
392 //TODO handle citation and microCitation
393 Reference citation
= null;
394 String microCitation
= null;
395 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
402 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
405 @Transactional(readOnly
= false)
406 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
408 clazz
= TaxonBase
.class;
410 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
415 protected void setDao(ITaxonDao dao
) {
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 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
433 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
435 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
436 if(numberOfResults
> 0) { // no point checking again
437 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
444 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
445 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
447 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
448 if(numberOfResults
> 0) { // no point checking again
449 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
455 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
456 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
458 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
459 if(numberOfResults
> 0) { // no point checking again
460 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
462 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
466 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
467 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
469 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
470 if(numberOfResults
> 0) { // no point checking again
471 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
477 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
478 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
480 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
481 if(numberOfResults
> 0) { // no point checking again
482 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
484 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
488 public List
<Taxon
> listAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
489 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
490 return pageAcceptedTaxaFor(synonymUuid
, classificationUuid
, pageSize
, pageNumber
, orderHints
, propertyPaths
).getRecords();
494 public Pager
<Taxon
> pageAcceptedTaxaFor(UUID synonymUuid
, UUID classificationUuid
, Integer pageSize
, Integer pageNumber
,
495 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
497 List
<Taxon
> list
= new ArrayList
<Taxon
>();
500 Synonym synonym
= null;
503 synonym
= (Synonym
) dao
.load(synonymUuid
);
504 } catch (ClassCastException e
){
505 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid
+ " is not a Synonmy");
506 } catch (NullPointerException e
){
507 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid
);
510 Classification classificationFilter
= null;
511 if(classificationUuid
!= null){
513 classificationFilter
= classificationDao
.load(classificationUuid
);
514 } catch (NullPointerException e
){
515 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid
);
517 if(classificationFilter
== null){
522 count
= dao
.countAcceptedTaxaFor(synonym
, classificationFilter
) ;
523 if(count
> (pageSize
* pageNumber
)){
524 list
= dao
.listAcceptedTaxaFor(synonym
, classificationFilter
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
527 return new DefaultPagerImpl
<Taxon
>(pageNumber
, count
.intValue(), pageSize
, list
);
532 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
533 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
535 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
536 relatedTaxa
.remove(taxon
);
537 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
543 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
544 * <code>taxon</code> supplied as parameter.
547 * @param includeRelationships
549 * @param maxDepth can be <code>null</code> for infinite depth
552 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
558 if(includeRelationships
.isEmpty()){
562 if(maxDepth
!= null) {
565 if(logger
.isDebugEnabled()){
566 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
568 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
569 for (TaxonRelationship taxRel
: taxonRelationships
) {
572 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
575 // filter by includeRelationships
576 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
577 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
578 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
579 if(logger
.isDebugEnabled()){
580 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
582 taxa
.add(taxRel
.getToTaxon());
583 if(maxDepth
== null || maxDepth
> 0) {
584 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
587 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
588 taxa
.add(taxRel
.getFromTaxon());
589 if(logger
.isDebugEnabled()){
590 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
592 if(maxDepth
== null || maxDepth
> 0) {
593 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
603 * @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)
606 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
607 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
609 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
610 if(numberOfResults
> 0) { // no point checking again
611 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
614 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
618 * @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)
621 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
622 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
624 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
625 if(numberOfResults
> 0) { // no point checking again
626 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
629 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
633 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
636 public List
<List
<Synonym
>> getSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
637 List
<List
<Synonym
>> result
= new ArrayList
<List
<Synonym
>>();
638 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
641 result
.add(t
.getHomotypicSynonymsByHomotypicGroup());
644 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
645 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
646 result
.add(t
.getSynonymsInGroup(homotypicalGroup
));
654 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
657 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
658 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
659 return t
.getHomotypicSynonymsByHomotypicGroup();
663 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
666 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
667 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
668 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
669 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
670 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
671 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
673 return heterotypicSynonymyGroups
;
677 public List
<UuidAndTitleCache
<IdentifiableEntity
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
679 List
<UuidAndTitleCache
<IdentifiableEntity
>> results
= new ArrayList
<UuidAndTitleCache
<IdentifiableEntity
>>();
682 if (configurator
.isDoSynonyms() || configurator
.isDoTaxa() || configurator
.isDoNamesWithoutTaxa()){
683 results
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.isDoNamesWithoutTaxa(), configurator
.isDoMisappliedNames(),configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
685 if (configurator
.isDoTaxaByCommonNames()) {
687 //if(configurator.getPageSize() == null ){
688 List
<UuidAndTitleCache
<IdentifiableEntity
>> commonNameResults
= dao
.getTaxaByCommonNameForEditor(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
689 if(commonNameResults
!= null){
690 results
.addAll(commonNameResults
);
698 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
701 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
703 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
704 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
705 List
<TaxonBase
> taxa
= null;
708 long numberTaxaResults
= 0L;
711 List
<String
> propertyPath
= new ArrayList
<String
>();
712 if(configurator
.getTaxonPropertyPath() != null){
713 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
717 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
718 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
720 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
721 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
722 configurator
.getNamedAreas());
725 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
726 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
727 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
728 configurator
.getMatchMode(), configurator
.getNamedAreas(),
729 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
733 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
736 results
.addAll(taxa
);
739 numberOfResults
+= numberTaxaResults
;
741 // Names without taxa
742 if (configurator
.isDoNamesWithoutTaxa()) {
743 int numberNameResults
= 0;
745 List
<?
extends TaxonNameBase
<?
,?
>> names
=
746 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
747 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
748 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
749 if (names
.size() > 0) {
750 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
751 if (taxonName
.getTaxonBases().size() == 0) {
752 results
.add(taxonName
);
756 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
757 numberOfResults
+= numberNameResults
;
761 // Taxa from common names
763 if (configurator
.isDoTaxaByCommonNames()) {
764 taxa
= new ArrayList
<TaxonBase
>();
765 numberTaxaResults
= 0;
766 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
767 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
769 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
770 List
<Taxon
> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
771 taxa
.addAll(commonNameResults
);
774 results
.addAll(taxa
);
776 numberOfResults
+= numberTaxaResults
;
780 return new DefaultPagerImpl
<IdentifiableEntity
>
781 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
784 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
785 return dao
.getUuidAndTitleCache();
789 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
792 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
793 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
794 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
795 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
796 for (TaxonDescription taxDesc
: descriptions
){
797 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
798 for (DescriptionElementBase descElem
: elements
){
799 for(Media media
: descElem
.getMedia()){
801 //find the best matching representation
802 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
811 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
814 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
815 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
820 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
823 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
824 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
825 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
827 logger
.trace("listMedia() - START");
829 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
830 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
831 List
<Media
> nonImageGalleryImages
= new ArrayList
<Media
>();
833 if (limitToGalleries
== null) {
834 limitToGalleries
= false;
837 // --- resolve related taxa
838 if (includeRelationships
!= null && ! includeRelationships
.isEmpty()) {
839 logger
.trace("listMedia() - resolve related taxa");
840 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
843 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
845 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
846 logger
.trace("listMedia() - includeTaxonDescriptions");
847 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
848 // --- TaxonDescriptions
849 for (Taxon t
: taxa
) {
850 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
852 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
853 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
854 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
855 for (Media media
: element
.getMedia()) {
856 if(taxonDescription
.isImageGallery()){
857 taxonMedia
.add(media
);
860 nonImageGalleryImages
.add(media
);
866 //put images from image gallery first (#3242)
867 taxonMedia
.addAll(nonImageGalleryImages
);
871 if(includeOccurrences
!= null && includeOccurrences
) {
872 logger
.trace("listMedia() - includeOccurrences");
873 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
875 for (Taxon t
: taxa
) {
876 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
878 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
880 // direct media removed from specimen #3597
881 // taxonMedia.addAll(occurrence.getMedia());
883 // SpecimenDescriptions
884 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
885 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
886 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
887 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
888 for (DescriptionElementBase element
: elements
) {
889 for (Media media
: element
.getMedia()) {
890 taxonMedia
.add(media
);
897 //TODO why may collections have media attached? #
898 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
899 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
900 if (derivedUnit
.getCollection() != null){
901 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
905 // pherograms & gelPhotos
906 if (occurrence
.isInstanceOf(DnaSample
.class)) {
907 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
908 Set
<Sequence
> sequences
= dnaSample
.getSequences();
909 //we do show only those gelPhotos which lead to a consensus sequence
910 for (Sequence sequence
: sequences
) {
911 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
912 for (SingleRead singleRead
: sequence
.getSingleReads()){
913 AmplificationResult amplification
= singleRead
.getAmplificationResult();
914 dnaRelatedMedia
.add(amplification
.getGelPhoto());
915 dnaRelatedMedia
.add(singleRead
.getPherogram());
916 dnaRelatedMedia
.remove(null);
918 taxonMedia
.addAll(dnaRelatedMedia
);
925 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
926 logger
.trace("listMedia() - includeTaxonNameDescriptions");
927 // --- TaxonNameDescription
928 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
929 for (Taxon t
: taxa
) {
930 nameDescriptions
.addAll(t
.getName().getDescriptions());
932 for(TaxonNameDescription nameDescription
: nameDescriptions
){
933 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
934 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
935 for (DescriptionElementBase element
: elements
) {
936 for (Media media
: element
.getMedia()) {
937 taxonMedia
.add(media
);
945 logger
.trace("listMedia() - initialize");
946 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
948 logger
.trace("listMedia() - END");
954 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
957 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
958 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
962 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
965 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
966 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
970 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
973 public int countAllRelationships() {
974 return this.dao
.countAllRelationships();
981 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
984 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
985 return this.dao
.findIdenticalTaxonNames(propertyPath
);
990 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
993 public DeleteResult
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) {
996 config
= new TaxonDeletionConfigurator();
999 DeleteResult result
= isDeletable(taxon
, config
);
1002 // --- DeleteSynonymRelations
1003 if (config
.isDeleteSynonymRelations()){
1004 boolean removeSynonymNameFromHomotypicalGroup
= false;
1005 // use tmp Set to avoid concurrent modification
1006 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
1007 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
1008 for (SynonymRelationship synRel
: synRelsToDelete
){
1009 Synonym synonym
= synRel
.getSynonym();
1010 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1011 // this will cause hibernate to delete the relationship since
1012 // the SynonymRelationship field on both is annotated with removeOrphan
1013 // so no further explicit deleting of the relationship should be done here
1014 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
1016 // --- DeleteSynonymsIfPossible
1017 if (config
.isDeleteSynonymsIfPossible()){
1019 boolean newHomotypicGroupIfNeeded
= true;
1020 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
1021 deleteSynonym(synonym
, taxon
, synConfig
);
1023 // relationship will be deleted by hibernate automatically,
1024 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1026 // deleteSynonymRelationships(synonym, taxon);
1031 // --- DeleteTaxonRelationships
1032 if (! config
.isDeleteTaxonRelationships()){
1033 if (taxon
.getTaxonRelations().size() > 0){
1034 String message
= "Taxon can't be deleted as it is related to another taxon. " +
1035 "Remove taxon from all relations to other taxa prior to deletion.";
1036 // throw new ReferencedObjectUndeletableException(message);
1039 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
1040 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
1041 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
1042 if (taxon
.equals(taxRel
.getToTaxon())){
1043 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
1047 taxon
.removeTaxonRelation(taxRel
);
1048 /*if (taxFrom.equals(taxon)){
1050 this.deleteTaxon(taxTo, taxConf, classification);
1051 } catch(DataChangeNoRollbackException e){
1052 logger.debug("A related taxon will not be deleted." + e.getMessage());
1056 this.deleteTaxon(taxFrom, taxConf, classification);
1057 } catch(DataChangeNoRollbackException e){
1058 logger.debug("A related taxon will not be deleted." + e.getMessage());
1066 if (config
.isDeleteDescriptions()){
1067 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1068 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1069 for (TaxonDescription desc
: descriptions
){
1070 //TODO use description delete configurator ?
1071 //FIXME check if description is ALWAYS deletable
1072 if (desc
.getDescribedSpecimenOrObservation() != null){
1073 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1074 " which also describes specimens or abservations";
1075 //throw new ReferencedObjectUndeletableException(message);
1077 removeDescriptions
.add(desc
);
1078 descriptionService
.delete(desc
);
1081 for (TaxonDescription desc
: removeDescriptions
){
1082 taxon
.removeDescription(desc
);
1087 /* //check references with only reverse mapping
1088 String message = checkForReferences(taxon);
1089 if (message != null){
1090 //throw new ReferencedObjectUndeletableException(message.toString());
1093 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1094 //if (taxon.getTaxonNodes().size() > 0){
1095 // 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.";
1096 // throw new ReferencedObjectUndeletableException(message);
1099 if (taxon
.getTaxonNodes().size() != 0){
1100 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1101 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1102 TaxonNode node
= null;
1103 boolean deleteChildren
;
1104 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1105 deleteChildren
= true;
1107 deleteChildren
= false;
1109 boolean success
= true;
1110 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1111 while (iterator
.hasNext()){
1112 node
= iterator
.next();
1113 if (node
.getClassification().equals(classification
)){
1119 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1120 nodeService
.delete(node
);
1123 result
.addException(new Exception("The taxon can not be deleted because it is not used in defined classification."));
1125 } else if (config
.isDeleteInAllClassifications()){
1126 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1127 nodesList
.addAll(taxon
.getTaxonNodes());
1129 for (ITaxonTreeNode treeNode
: nodesList
){
1130 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1131 if(!deleteChildren
){
1132 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1133 //nodesList.addAll(taxonNode.getChildNodes());
1134 for (Object childNode: childNodes){
1135 TaxonNode childNodeCast = (TaxonNode) childNode;
1136 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1140 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1141 deleteTaxon(childNode.getTaxon(), config, classification);
1144 // taxon.removeTaxonNode(taxonNode);
1145 //nodeService.delete(taxonNode);
1148 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1149 for (Object childNode
: childNodes
){
1150 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1151 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1154 //taxon.removeTaxonNode(taxonNode);
1157 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1158 DeleteResult resultNodes
= nodeService
.deleteTaxonNodes(nodesList
, config
);
1159 if (!resultNodes
.isOk()){
1160 result
.addExceptions(resultNodes
.getExceptions());
1161 result
.setStatus(resultNodes
.getStatus());
1166 result
.addException(new Exception("The taxon can not be deleted because the taxon node can not be removed."));
1172 //PolytomousKey TODO
1176 if (config
.isDeleteNameIfPossible()){
1179 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1180 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1181 //check whether taxon will be deleted or not
1182 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null ){
1183 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1184 //name.removeTaxonBase(taxon);
1185 //nameService.saveOrUpdate(name);
1186 taxon
.setName(null);
1187 //dao.delete(taxon);
1188 DeleteResult nameResult
= new DeleteResult();
1190 //remove name if possible (and required)
1191 if (name
!= null && config
.isDeleteNameIfPossible()){
1193 nameResult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1199 if (nameResult
.isError()){
1200 //result.setError();
1201 result
.addRelatedObject(name
);
1202 result
.addExceptions(nameResult
.getExceptions());
1210 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1212 for (TaxonDescription desc: descriptions){
1213 if (config.isDeleteDescriptions()){
1214 //TODO use description delete configurator ?
1215 //FIXME check if description is ALWAYS deletable
1216 taxon.removeDescription(desc);
1217 descriptionService.delete(desc);
1219 if (desc.getDescribedSpecimenOrObservations().size()>0){
1220 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1221 " which also describes specimens or observations";
1222 throw new ReferencedObjectUndeletableException(message);
1227 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) ){
1229 UUID uuid
= dao
.delete(taxon
);
1231 }catch(Exception e
){
1232 result
.addException(e
);
1238 result
.addException(new Exception("The Taxon can't be deleted."));
1243 // List<Exception> exceptions = new ArrayList<Exception>();
1244 // for (String message: referencedObjects){
1245 // ReferencedObjectUndeletableException exception = new ReferencedObjectUndeletableException(message);
1246 // exceptions.add(exception);
1248 // result.addExceptions(exceptions);
1249 // result.setError();
1256 private String
checkForReferences(Taxon taxon
){
1257 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1258 for (CdmBase referencingObject
: referencingObjects
){
1259 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1260 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1261 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";
1267 /* //PolytomousKeyNode
1268 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1269 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1274 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1275 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1280 if (referencingObject
.isInstanceOf(DeterminationEvent
.class)){
1281 String message
= "Taxon can't be deleted as it is used in a determination event";
1287 referencingObjects
= null;
1291 private boolean checkForPolytomousKeys(Taxon taxon
){
1292 boolean result
= false;
1293 List
<CdmBase
> list
= genericDao
.getCdmBasesByFieldAndClass(PolytomousKeyNode
.class, "taxon", taxon
);
1294 if (!list
.isEmpty()) {
1300 @Transactional(readOnly
= false)
1301 public UUID
delete(Synonym syn
){
1302 UUID result
= syn
.getUuid();
1303 this.deleteSynonym(syn
, null);
1310 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1312 @Transactional(readOnly
= false)
1314 public DeleteResult
deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1315 return deleteSynonym(synonym
, null, config
);
1321 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1323 @Transactional(readOnly
= false)
1325 public DeleteResult
deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1326 DeleteResult result
= new DeleteResult();
1327 if (synonym
== null){
1332 if (config
== null){
1333 config
= new SynonymDeletionConfigurator();
1335 result
= isDeletable(synonym
, config
);
1339 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1341 //remove synonymRelationship
1342 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1344 taxonSet
.add(taxon
);
1346 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1348 for (Taxon relatedTaxon
: taxonSet
){
1349 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1350 relatedTaxon
.removeSynonym(synonym
, false);
1351 this.saveOrUpdate(relatedTaxon
);
1353 this.saveOrUpdate(synonym
);
1355 //TODO remove name from homotypical group?
1357 //remove synonym (if necessary)
1360 if (synonym
.getSynonymRelations().isEmpty()){
1361 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1362 synonym
.setName(null);
1363 uuid
= dao
.delete(synonym
);
1365 //remove name if possible (and required)
1366 if (name
!= null && config
.isDeleteNameIfPossible()){
1368 DeleteResult nameDeleteresult
= nameService
.delete(name
, config
.getNameDeletionConfig());
1369 if (nameDeleteresult
.isAbort()){
1370 result
.addExceptions(nameDeleteresult
.getExceptions());
1371 result
.addUpdatedObject(name
);
1378 result
.addException(new ReferencedObjectUndeletableException("Synonym can not be deleted it is used in a synonymRelationship."));
1386 // List<Exception> exceptions = new ArrayList<Exception>();
1387 // for (String message :messages){
1388 // exceptions.add(new ReferencedObjectUndeletableException(message));
1390 // result.setError();
1391 // result.addExceptions(exceptions);
1400 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1403 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1405 return this.dao
.findIdenticalNamesNew(propertyPath
);
1409 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1412 public String
getPhylumName(TaxonNameBase name
){
1413 return this.dao
.getPhylumName(name
);
1417 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1420 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1421 return dao
.deleteSynonymRelationships(syn
, taxon
);
1425 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1428 public long deleteSynonymRelationships(Synonym syn
) {
1429 return dao
.deleteSynonymRelationships(syn
, null);
1434 * @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)
1437 public List
<SynonymRelationship
> listSynonymRelationships(
1438 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1439 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1440 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1442 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1443 if(numberOfResults
> 0) { // no point checking again
1444 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1450 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1453 public Taxon
findBestMatchingTaxon(String taxonName
) {
1454 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1455 config
.setTaxonNameTitle(taxonName
);
1456 return findBestMatchingTaxon(config
);
1462 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1464 Taxon bestCandidate
= null;
1466 // 1. search for acceptet taxa
1467 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1468 boolean bestCandidateMatchesSecUuid
= false;
1469 boolean bestCandidateIsInClassification
= false;
1470 int countEqualCandidates
= 0;
1471 for(TaxonBase taxonBaseCandidate
: taxonList
){
1472 if(taxonBaseCandidate
instanceof Taxon
){
1473 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1474 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1475 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1477 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1478 bestCandidate
= newCanditate
;
1479 countEqualCandidates
= 1;
1480 bestCandidateMatchesSecUuid
= true;
1484 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1485 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1487 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1488 bestCandidate
= newCanditate
;
1489 countEqualCandidates
= 1;
1490 bestCandidateIsInClassification
= true;
1493 if (bestCandidate
== null){
1494 bestCandidate
= newCanditate
;
1495 countEqualCandidates
= 1;
1499 }else{ //not Taxon.class
1502 countEqualCandidates
++;
1505 if (bestCandidate
!= null){
1506 if(countEqualCandidates
> 1){
1507 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1508 return bestCandidate
;
1510 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1511 return bestCandidate
;
1516 // 2. search for synonyms
1517 if (config
.isIncludeSynonyms()){
1518 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1519 for(TaxonBase taxonBase
: synonymList
){
1520 if(taxonBase
instanceof Synonym
){
1521 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1522 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1523 if(!acceptetdCandidates
.isEmpty()){
1524 bestCandidate
= acceptetdCandidates
.iterator().next();
1525 if(acceptetdCandidates
.size() == 1){
1526 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1527 return bestCandidate
;
1529 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1530 return bestCandidate
;
1532 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1538 } catch (Exception e
){
1540 e
.printStackTrace();
1543 return bestCandidate
;
1546 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1547 UUID configClassificationUuid
= config
.getClassificationUuid();
1548 if (configClassificationUuid
== null){
1551 for (TaxonNode node
: taxon
.getTaxonNodes()){
1552 UUID classUuid
= node
.getClassification().getUuid();
1553 if (configClassificationUuid
.equals(classUuid
)){
1560 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1561 UUID configSecUuid
= config
.getSecUuid();
1562 if (configSecUuid
== null){
1565 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1566 return configSecUuid
.equals(taxonSecUuid
);
1570 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1573 public Synonym
findBestMatchingSynonym(String taxonName
) {
1574 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1575 if(! synonymList
.isEmpty()){
1576 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1577 if(synonymList
.size() == 1){
1578 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1581 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1590 * @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)
1593 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1594 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1596 Synonym synonym
= oldSynonymRelation
.getSynonym();
1597 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1598 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1599 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1600 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1601 //set default relationship type
1602 if (newSynonymRelationshipType
== null){
1603 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1605 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1607 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1608 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1609 boolean isSingleInGroup
= !(hgSize
> 1);
1611 if (! isSingleInGroup
){
1612 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1613 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1614 if (isHomotypicToAccepted
){
1615 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.";
1616 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1617 message
= String
.format(message
, homotypicRelatives
);
1618 throw new HomotypicalGroupChangeException(message
);
1620 if (! moveHomotypicGroup
){
1621 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.";
1622 throw new HomotypicalGroupChangeException(message
);
1625 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1627 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1629 SynonymRelationship result
= null;
1630 //move all synonyms to new taxon
1631 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1632 for (Synonym syn
: homotypicSynonyms
){
1633 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1634 for (SynonymRelationship synRelation
: synRelations
){
1635 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1636 Reference
<?
> newReference
= reference
;
1637 if (newReference
== null && keepReference
){
1638 newReference
= synRelation
.getCitation();
1640 String newRefDetail
= referenceDetail
;
1641 if (newRefDetail
== null && keepReference
){
1642 newRefDetail
= synRelation
.getCitationMicroReference();
1644 newTaxon
= HibernateProxyHelper
.deproxy(newTaxon
, Taxon
.class);
1645 fromTaxon
= HibernateProxyHelper
.deproxy(fromTaxon
, Taxon
.class);
1646 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1647 fromTaxon
.removeSynonymRelation(synRelation
, false);
1649 //change homotypic group of synonym if relType is 'homotypic'
1650 // if (newRelTypeIsHomotypic){
1651 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1654 if (synRelation
.equals(oldSynonymRelation
)){
1655 result
= newSynRelation
;
1661 saveOrUpdate(fromTaxon
);
1662 saveOrUpdate(newTaxon
);
1663 //Assert that there is a result
1664 if (result
== null){
1665 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1666 throw new IllegalStateException(message
);
1672 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1675 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1676 return dao
.getUuidAndTitleCacheTaxon();
1680 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1683 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1684 return dao
.getUuidAndTitleCacheSynonym();
1688 * @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)
1691 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1692 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1693 Classification classification
, List
<Language
> languages
,
1694 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1697 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1699 // --- execute search
1700 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1702 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1703 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1705 // --- initialize taxa, thighlight matches ....
1706 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1707 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1708 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1710 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1711 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1715 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTerm
> statusFilter
,
1716 Classification classification
,
1717 Integer pageSize
, Integer pageNumber
,
1718 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1720 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1722 // --- execute search
1723 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1725 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1726 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1728 // --- initialize taxa, thighlight matches ....
1729 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1730 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1731 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1733 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1734 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1739 * @param queryString
1740 * @param classification
1742 * @param highlightFragments
1743 * @param sortFields TODO
1744 * @param directorySelectClass
1747 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1748 boolean highlightFragments
, SortField
[] sortFields
) {
1749 BooleanQuery finalQuery
= new BooleanQuery();
1750 BooleanQuery textQuery
= new BooleanQuery();
1752 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1753 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1755 if(sortFields
== null){
1756 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1758 luceneSearch
.setSortFields(sortFields
);
1760 // ---- search criteria
1761 luceneSearch
.setCdmTypRestriction(clazz
);
1763 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1764 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1766 finalQuery
.add(textQuery
, Occur
.MUST
);
1768 if(classification
!= null){
1769 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1771 luceneSearch
.setQuery(finalQuery
);
1773 if(highlightFragments
){
1774 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1776 return luceneSearch
;
1780 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1781 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1782 * drawback of requiring to do the join an indexing time.
1783 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1785 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1787 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1788 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1790 * @param queryString
1791 * @param classification
1793 * @param highlightFragments
1794 * @param sortFields TODO
1797 * @throws IOException
1799 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1800 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1803 String queryTermField
;
1804 String toField
= "id"; // TaxonBase.uuid
1806 if(edge
.isBidirectional()){
1807 throw new RuntimeException("Bidirectional joining not supported!");
1810 fromField
= "relatedFrom.id";
1811 queryTermField
= "relatedFrom.titleCache";
1812 } else if(edge
.isInvers()) {
1813 fromField
= "relatedTo.id";
1814 queryTermField
= "relatedTo.titleCache";
1816 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1819 BooleanQuery finalQuery
= new BooleanQuery();
1821 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1822 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1824 BooleanQuery joinFromQuery
= new BooleanQuery();
1825 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1826 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1827 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1829 if(sortFields
== null){
1830 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1832 luceneSearch
.setSortFields(sortFields
);
1834 finalQuery
.add(joinQuery
, Occur
.MUST
);
1836 if(classification
!= null){
1837 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1839 luceneSearch
.setQuery(finalQuery
);
1841 if(highlightFragments
){
1842 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1844 return luceneSearch
;
1851 * @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)
1854 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1855 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1856 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTerm
> distributionStatus
, List
<Language
> languages
,
1857 boolean highlightFragments
, Integer pageSize
,
1858 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1859 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1861 // FIXME: allow taxonomic ordering
1862 // 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";
1863 // this require building a special sort column by a special classBridge
1864 if(highlightFragments
){
1865 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1866 "currently not fully supported by this method and thus " +
1867 "may not work with common names and misapplied names.");
1870 // convert sets to lists
1871 List
<NamedArea
> namedAreaList
= null;
1872 List
<PresenceAbsenceTerm
>distributionStatusList
= null;
1873 if(namedAreas
!= null){
1874 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1875 namedAreaList
.addAll(namedAreas
);
1877 if(distributionStatus
!= null){
1878 distributionStatusList
= new ArrayList
<PresenceAbsenceTerm
>(distributionStatus
.size());
1879 distributionStatusList
.addAll(distributionStatus
);
1882 // set default if parameter is null
1883 if(searchModes
== null){
1884 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1887 // set sort order and thus override any sort orders which may have been
1888 // defindes by prepare*Search methods
1889 if(orderHints
== null){
1890 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1892 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1894 for(OrderHint oh
: orderHints
){
1895 sortFields
[i
++] = oh
.toSortField();
1897 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1898 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1901 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1903 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1904 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1907 ======== filtering by distribution , HOWTO ========
1909 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1910 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1911 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1912 which will be put into a FilteredQuersy in the end ?
1915 3. how does it work in spatial?
1917 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1918 - http://www.infoq.com/articles/LuceneSpatialSupport
1919 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1920 ------------------------------------------------------------------------
1923 A) use a separate distribution filter per index sub-query/search:
1924 - byTaxonSyonym (query TaxaonBase):
1925 use a join area filter (Distribution -> TaxonBase)
1926 - byCommonName (query DescriptionElementBase): use an area filter on
1927 DescriptionElementBase !!! PROBLEM !!!
1928 This cannot work since the distributions are different entities than the
1929 common names and thus these are different lucene documents.
1930 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1931 use a join area filter (Distribution -> TaxonBase)
1933 B) use a common distribution filter for all index sub-query/searches:
1934 - use a common join area filter (Distribution -> TaxonBase)
1935 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1936 PROBLEM in this case: we are losing the fragment highlighting for the
1937 common names, since the returned documents are always TaxonBases
1940 /* The QueryFactory for creating filter queries on Distributions should
1941 * The query factory used for the common names query cannot be reused
1942 * for this case, since we want to only record the text fields which are
1943 * actually used in the primary query
1945 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1947 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1950 // search for taxa or synonyms
1951 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1952 Class taxonBaseSubclass
= TaxonBase
.class;
1953 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1954 taxonBaseSubclass
= Taxon
.class;
1955 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1956 taxonBaseSubclass
= Synonym
.class;
1958 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1959 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1960 /* A) does not work!!!!
1961 if(addDistributionFilter){
1962 // in this case we need a filter which uses a join query
1963 // to get the TaxonBase documents for the DescriptionElementBase documents
1964 // which are matching the areas in question
1965 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1967 distributionStatusList,
1968 distributionFilterQueryFactory
1970 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1973 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1974 // add additional area filter for synonyms
1975 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1976 String toField
= "accTaxon.id"; // id in TaxonBase index
1978 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1980 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1981 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1986 // search by CommonTaxonName
1987 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1989 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1990 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1991 "inDescription.taxon.id",
1993 QueryFactory
.addTypeRestriction(
1994 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1995 , CommonTaxonName
.class
1997 CommonTaxonName
.class);
1998 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1999 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2000 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
2001 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
2002 byCommonNameSearch
.setSortFields(sortFields
);
2003 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2005 luceneSearches
.add(byCommonNameSearch
);
2007 /* A) does not work!!!!
2009 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
2010 queryString, classification, null, languages, highlightFragments)
2012 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2013 if(addDistributionFilter){
2014 // in this case we are able to use DescriptionElementBase documents
2015 // which are matching the areas in question directly
2016 BooleanQuery byDistributionQuery = createByDistributionQuery(
2018 distributionStatusList,
2019 distributionFilterQueryFactory
2021 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2025 // search by misapplied names
2026 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
2028 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2029 // which allows doing query time joins
2030 // finds the misapplied name (Taxon B) which is an misapplication for
2031 // a related Taxon A.
2033 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
2034 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
2035 queryString
, classification
, languages
, highlightFragments
, sortFields
));
2036 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2038 if(addDistributionFilter
){
2039 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2042 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2043 * Maybe this is a but in java itself java.
2045 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2048 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2050 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2051 * will execute as expected:
2053 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2054 * String toField = "relation." + misappliedNameForUuid +".to.id";
2056 * Comparing both strings by the String.equals method returns true, so both String are identical.
2058 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2059 * 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)
2060 * The bug is persistent after a reboot of the development computer.
2062 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2063 // String toField = "relation." + misappliedNameForUuid +".to.id";
2064 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2065 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2066 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2068 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
2069 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2070 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
2072 // debug code for bug described above
2073 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
2074 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2076 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
2080 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
2081 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
2084 if(addDistributionFilter
){
2087 // in this case we need a filter which uses a join query
2088 // to get the TaxonBase documents for the DescriptionElementBase documents
2089 // which are matching the areas in question
2091 // for toTaxa, doByCommonName
2092 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
2094 distributionStatusList
,
2095 distributionFilterQueryFactory
2097 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
2100 if (addDistributionFilter
){
2101 multiSearch
.setFilter(multiIndexByAreaFilter
);
2105 // --- execute search
2106 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2108 // --- initialize taxa, highlight matches ....
2109 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2112 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2113 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2115 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2116 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2120 * @param namedAreaList at least one area must be in the list
2121 * @param distributionStatusList optional
2123 * @throws IOException
2125 protected Query
createByDistributionJoinQuery(
2126 List
<NamedArea
> namedAreaList
,
2127 List
<PresenceAbsenceTerm
> distributionStatusList
,
2128 QueryFactory queryFactory
2129 ) throws IOException
{
2131 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
2132 String toField
= "id"; // id in TaxonBase index
2134 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
2136 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
2138 return taxonAreaJoinQuery
;
2142 * @param namedAreaList
2143 * @param distributionStatusList
2144 * @param queryFactory
2147 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2148 List
<PresenceAbsenceTerm
> distributionStatusList
, QueryFactory queryFactory
) {
2149 BooleanQuery areaQuery
= new BooleanQuery();
2150 // area field from Distribution
2151 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2153 // status field from Distribution
2154 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2155 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2158 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2163 * This method has been primarily created for testing the area join query but might
2164 * also be useful in other situations
2166 * @param namedAreaList
2167 * @param distributionStatusList
2168 * @param classification
2169 * @param highlightFragments
2171 * @throws IOException
2173 protected LuceneSearch
prepareByDistributionSearch(
2174 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTerm
> distributionStatusList
,
2175 Classification classification
) throws IOException
{
2177 BooleanQuery finalQuery
= new BooleanQuery();
2179 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2181 // FIXME is this query factory using the wrong type?
2182 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2184 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2185 luceneSearch
.setSortFields(sortFields
);
2188 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2190 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2192 if(classification
!= null){
2193 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2196 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2197 luceneSearch
.setQuery(finalQuery
);
2199 return luceneSearch
;
2205 * @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)
2208 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2209 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2210 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2211 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2214 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2216 // --- execute search
2217 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2219 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2220 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2222 // --- initialize taxa, highlight matches ....
2223 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2224 @SuppressWarnings("rawtypes")
2225 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2226 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2228 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2229 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2235 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2236 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2237 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2239 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2240 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2242 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2244 // --- execute search
2245 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2247 // --- initialize taxa, highlight matches ....
2248 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2250 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2251 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2252 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2254 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2255 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2257 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2258 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2265 * @param queryString
2266 * @param classification
2269 * @param highlightFragments
2270 * @param directorySelectClass
2273 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2274 String queryString
, Classification classification
, List
<Feature
> features
,
2275 List
<Language
> languages
, boolean highlightFragments
) {
2277 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2278 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2280 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2282 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2283 languages
, descriptionElementQueryFactory
);
2285 luceneSearch
.setSortFields(sortFields
);
2286 luceneSearch
.setCdmTypRestriction(clazz
);
2287 luceneSearch
.setQuery(finalQuery
);
2288 if(highlightFragments
){
2289 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2292 return luceneSearch
;
2296 * @param queryString
2297 * @param classification
2300 * @param descriptionElementQueryFactory
2303 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2304 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2305 BooleanQuery finalQuery
= new BooleanQuery();
2306 BooleanQuery textQuery
= new BooleanQuery();
2307 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2311 if(languages
== null || languages
.size() == 0){
2312 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2314 nameQuery
= new BooleanQuery();
2315 BooleanQuery languageSubQuery
= new BooleanQuery();
2316 for(Language lang
: languages
){
2317 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2319 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2320 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2322 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2325 // text field from TextData
2326 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2328 // --- TermBase fields - by representation ----
2329 // state field from CategoricalData
2330 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2332 // state field from CategoricalData
2333 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2335 // area field from Distribution
2336 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2338 // status field from Distribution
2339 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2341 finalQuery
.add(textQuery
, Occur
.MUST
);
2342 // --- classification ----
2344 if(classification
!= null){
2345 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2348 // --- IdentifieableEntity fields - by uuid
2349 if(features
!= null && features
.size() > 0 ){
2350 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2353 // the description must be associated with a taxon
2354 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2356 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2361 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2362 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2363 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2365 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2366 * or {@link MultilanguageTextFieldBridge }
2367 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2368 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2369 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2371 * TODO move to utiliy class !!!!!!!!
2373 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2375 if(stringBuilder
== null){
2376 stringBuilder
= new StringBuilder();
2378 if(languages
== null || languages
.size() == 0){
2379 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2381 for(Language lang
: languages
){
2382 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2385 return stringBuilder
;
2389 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2390 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2391 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2393 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2396 UUID nameUuid
= taxon
.getName().getUuid();
2397 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2398 String epithetOfTaxon
= null;
2399 String infragenericEpithetOfTaxon
= null;
2400 String infraspecificEpithetOfTaxon
= null;
2401 if (taxonName
.isSpecies()){
2402 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2403 } else if (taxonName
.isInfraGeneric()){
2404 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2405 } else if (taxonName
.isInfraSpecific()){
2406 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2408 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2409 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2410 List
<String
> taxonNames
= new ArrayList
<String
>();
2412 for (TaxonNode node
: nodes
){
2413 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2414 // List<String> synonymsEpithet = new ArrayList<String>();
2416 if (node
.getClassification().equals(classification
)){
2417 if (!node
.isTopmostNode()){
2418 TaxonNode parent
= node
.getParent();
2419 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2420 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2421 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2422 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2423 Rank rankOfTaxon
= taxonName
.getRank();
2426 //create inferred synonyms for species, subspecies
2427 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2429 Synonym inferredEpithet
= null;
2430 Synonym inferredGenus
= null;
2431 Synonym potentialCombination
= null;
2433 List
<String
> propertyPaths
= new ArrayList
<String
>();
2434 propertyPaths
.add("synonym");
2435 propertyPaths
.add("synonym.name");
2436 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2437 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2439 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2440 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2442 List
<TaxonRelationship
> taxonRelListParent
= null;
2443 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2444 if (doWithMisappliedNames
){
2445 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2446 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2450 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2453 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2454 Synonym syn
= synonymRelationOfParent
.getSynonym();
2456 inferredEpithet
= createInferredEpithets(taxon
,
2457 zooHashMap
, taxonName
, epithetOfTaxon
,
2458 infragenericEpithetOfTaxon
,
2459 infraspecificEpithetOfTaxon
,
2460 taxonNames
, parentName
,
2464 inferredSynonyms
.add(inferredEpithet
);
2465 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2466 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2469 if (doWithMisappliedNames
){
2471 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2472 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2474 inferredEpithet
= createInferredEpithets(taxon
,
2475 zooHashMap
, taxonName
, epithetOfTaxon
,
2476 infragenericEpithetOfTaxon
,
2477 infraspecificEpithetOfTaxon
,
2478 taxonNames
, parentName
,
2481 inferredSynonyms
.add(inferredEpithet
);
2482 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2483 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2487 if (!taxonNames
.isEmpty()){
2488 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2489 ZoologicalName name
;
2490 if (!synNotInCDM
.isEmpty()){
2491 inferredSynonymsToBeRemoved
.clear();
2493 for (Synonym syn
:inferredSynonyms
){
2494 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2495 if (!synNotInCDM
.contains(name
.getNameCache())){
2496 inferredSynonymsToBeRemoved
.add(syn
);
2500 // Remove identified Synonyms from inferredSynonyms
2501 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2502 inferredSynonyms
.remove(synonym
);
2507 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2510 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2511 TaxonNameBase synName
;
2512 ZoologicalName inferredSynName
;
2514 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2515 inferredGenus
= createInferredGenus(taxon
,
2516 zooHashMap
, taxonName
, epithetOfTaxon
,
2517 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2519 inferredSynonyms
.add(inferredGenus
);
2520 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2521 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2526 if (doWithMisappliedNames
){
2528 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2529 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2530 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2532 inferredSynonyms
.add(inferredGenus
);
2533 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2534 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2539 if (!taxonNames
.isEmpty()){
2540 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2541 ZoologicalName name
;
2542 if (!synNotInCDM
.isEmpty()){
2543 inferredSynonymsToBeRemoved
.clear();
2545 for (Synonym syn
:inferredSynonyms
){
2546 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2547 if (!synNotInCDM
.contains(name
.getNameCache())){
2548 inferredSynonymsToBeRemoved
.add(syn
);
2552 // Remove identified Synonyms from inferredSynonyms
2553 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2554 inferredSynonyms
.remove(synonym
);
2559 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2561 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2562 ZoologicalName inferredSynName
;
2563 //for all synonyms of the parent...
2564 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2565 TaxonNameBase synName
;
2566 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2567 synName
= synParent
.getName();
2569 HibernateProxyHelper
.deproxy(synParent
);
2571 // Set the sourceReference
2572 sourceReference
= synParent
.getSec();
2574 // Determine the idInSource
2575 String idInSourceParent
= getIdInSource(synParent
);
2577 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2578 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2579 String synParentInfragenericName
= null;
2580 String synParentSpecificEpithet
= null;
2582 if (parentSynZooName
.isInfraGeneric()){
2583 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2585 if (parentSynZooName
.isSpecies()){
2586 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2589 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2590 synonymsGenus.put(synGenusName, idInSource);
2593 //for all synonyms of the taxon
2595 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2597 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2598 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2599 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2601 synParentInfragenericName
,
2602 synParentSpecificEpithet
, syn
, zooHashMap
);
2604 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2605 inferredSynonyms
.add(potentialCombination
);
2606 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2607 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2614 if (doWithMisappliedNames
){
2616 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2618 TaxonNameBase misappliedParentName
;
2620 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2621 misappliedParentName
= misappliedParent
.getName();
2623 HibernateProxyHelper
.deproxy(misappliedParent
);
2625 // Set the sourceReference
2626 sourceReference
= misappliedParent
.getSec();
2628 // Determine the idInSource
2629 String idInSourceParent
= getIdInSource(misappliedParent
);
2631 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2632 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2633 String synParentInfragenericName
= null;
2634 String synParentSpecificEpithet
= null;
2636 if (parentSynZooName
.isInfraGeneric()){
2637 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2639 if (parentSynZooName
.isSpecies()){
2640 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2644 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2645 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2646 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2647 potentialCombination
= createPotentialCombination(
2648 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2650 synParentInfragenericName
,
2651 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2654 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2655 inferredSynonyms
.add(potentialCombination
);
2656 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2657 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2662 if (!taxonNames
.isEmpty()){
2663 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2664 ZoologicalName name
;
2665 if (!synNotInCDM
.isEmpty()){
2666 inferredSynonymsToBeRemoved
.clear();
2667 for (Synonym syn
:inferredSynonyms
){
2669 name
= (ZoologicalName
) syn
.getName();
2670 }catch (ClassCastException e
){
2671 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2673 if (!synNotInCDM
.contains(name
.getNameCache())){
2674 inferredSynonymsToBeRemoved
.add(syn
);
2677 // Remove identified Synonyms from inferredSynonyms
2678 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2679 inferredSynonyms
.remove(synonym
);
2685 logger
.info("The synonymrelationship type is not defined.");
2686 return inferredSynonyms
;
2693 return inferredSynonyms
;
2696 private Synonym
createPotentialCombination(String idInSourceParent
,
2697 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2698 String synParentInfragenericName
, String synParentSpecificEpithet
,
2699 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2700 Synonym potentialCombination
;
2701 Reference sourceReference
;
2702 ZoologicalName inferredSynName
;
2703 HibernateProxyHelper
.deproxy(syn
);
2705 // Set sourceReference
2706 sourceReference
= syn
.getSec();
2707 if (sourceReference
== null){
2708 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2710 if (!parentSynZooName
.getTaxa().isEmpty()){
2711 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2713 sourceReference
= taxon
.getSec();
2716 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2718 String synTaxonInfraSpecificName
= null;
2720 if (parentSynZooName
.isSpecies()){
2721 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2724 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2725 synonymsEpithet.add(epithetName);
2728 //create potential combinations...
2729 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2731 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2732 if (zooSynName
.isSpecies()){
2733 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2734 if (parentSynZooName
.isInfraGeneric()){
2735 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2738 if (zooSynName
.isInfraSpecific()){
2739 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2740 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2742 if (parentSynZooName
.isInfraGeneric()){
2743 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2747 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2749 // Set the sourceReference
2750 potentialCombination
.setSec(sourceReference
);
2753 // Determine the idInSource
2754 String idInSourceSyn
= getIdInSource(syn
);
2756 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2757 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2758 inferredSynName
.addSource(originalSource
);
2759 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2760 potentialCombination
.addSource(originalSource
);
2763 return potentialCombination
;
2766 private Synonym
createInferredGenus(Taxon taxon
,
2767 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2768 String epithetOfTaxon
, String genusOfTaxon
,
2769 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2772 Synonym inferredGenus
;
2773 TaxonNameBase synName
;
2774 ZoologicalName inferredSynName
;
2775 synName
=syn
.getName();
2776 HibernateProxyHelper
.deproxy(syn
);
2778 // Determine the idInSource
2779 String idInSourceSyn
= getIdInSource(syn
);
2780 String idInSourceTaxon
= getIdInSource(taxon
);
2781 // Determine the sourceReference
2782 Reference sourceReference
= syn
.getSec();
2784 //logger.warn(sourceReference.getTitleCache());
2786 synName
= syn
.getName();
2787 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2788 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2789 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2790 synonymsEpithet.add(synSpeciesEpithetName);
2793 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2794 //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...
2797 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2798 if (zooParentName
.isInfraGeneric()){
2799 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2802 if (taxonName
.isSpecies()){
2803 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2805 if (taxonName
.isInfraSpecific()){
2806 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2807 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2811 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2813 // Set the sourceReference
2814 inferredGenus
.setSec(sourceReference
);
2816 // Add the original source
2817 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2818 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2819 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2820 inferredGenus
.addSource(originalSource
);
2822 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2823 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2824 inferredSynName
.addSource(originalSource
);
2825 originalSource
= null;
2828 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2829 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2830 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2831 inferredGenus
.addSource(originalSource
);
2833 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2834 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2835 inferredSynName
.addSource(originalSource
);
2836 originalSource
= null;
2839 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2841 return inferredGenus
;
2844 private Synonym
createInferredEpithets(Taxon taxon
,
2845 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2846 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2847 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2848 TaxonNameBase parentName
, TaxonBase syn
) {
2850 Synonym inferredEpithet
;
2851 TaxonNameBase
<?
,?
> synName
;
2852 ZoologicalName inferredSynName
;
2853 HibernateProxyHelper
.deproxy(syn
);
2855 // Determine the idInSource
2856 String idInSourceSyn
= getIdInSource(syn
);
2857 String idInSourceTaxon
= getIdInSource(taxon
);
2858 // Determine the sourceReference
2859 Reference
<?
> sourceReference
= syn
.getSec();
2861 if (sourceReference
== null){
2862 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2863 sourceReference
= taxon
.getSec();
2866 synName
= syn
.getName();
2867 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2868 String synGenusName
= zooSynName
.getGenusOrUninomial();
2869 String synInfraGenericEpithet
= null;
2870 String synSpecificEpithet
= null;
2872 if (zooSynName
.getInfraGenericEpithet() != null){
2873 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2876 if (zooSynName
.isInfraSpecific()){
2877 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2880 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2881 synonymsGenus.put(synGenusName, idInSource);
2884 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2886 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2887 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2888 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2890 inferredSynName
.setGenusOrUninomial(synGenusName
);
2892 if (parentName
.isInfraGeneric()){
2893 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2895 if (taxonName
.isSpecies()){
2896 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2897 }else if (taxonName
.isInfraSpecific()){
2898 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2899 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2902 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2904 // Set the sourceReference
2905 inferredEpithet
.setSec(sourceReference
);
2907 /* Add the original source
2908 if (idInSource != null) {
2909 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2912 Reference citation = getCitation(syn);
2913 if (citation != null) {
2914 originalSource.setCitation(citation);
2915 inferredEpithet.addSource(originalSource);
2918 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2921 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2922 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2924 inferredEpithet
.addSource(originalSource
);
2926 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2927 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2929 inferredSynName
.addSource(originalSource
);
2933 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2935 return inferredEpithet
;
2939 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2940 * Very likely only useful for createInferredSynonyms().
2945 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2946 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2947 if (taxonName
== null) {
2948 taxonName
= zooHashMap
.get(uuid
);
2954 * Returns the idInSource for a given Synonym.
2957 private String
getIdInSource(TaxonBase taxonBase
) {
2958 String idInSource
= null;
2959 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2960 if (sources
.size() == 1) {
2961 IdentifiableSource source
= sources
.iterator().next();
2962 if (source
!= null) {
2963 idInSource
= source
.getIdInSource();
2965 } else if (sources
.size() > 1) {
2968 for (IdentifiableSource source
: sources
) {
2969 idInSource
+= source
.getIdInSource();
2970 if (count
< sources
.size()) {
2975 } else if (sources
.size() == 0){
2976 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2985 * Returns the citation for a given Synonym.
2988 private Reference
getCitation(Synonym syn
) {
2989 Reference citation
= null;
2990 Set
<IdentifiableSource
> sources
= syn
.getSources();
2991 if (sources
.size() == 1) {
2992 IdentifiableSource source
= sources
.iterator().next();
2993 if (source
!= null) {
2994 citation
= source
.getCitation();
2996 } else if (sources
.size() > 1) {
2997 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
3004 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
3005 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
3007 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
3008 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
3009 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
3011 return inferredSynonyms
;
3015 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
3017 // TODO quickly implemented, create according dao !!!!
3018 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
3019 Set
<Classification
> classifications
= new HashSet
<Classification
>();
3020 List
<Classification
> list
= new ArrayList
<Classification
>();
3022 if (taxonBase
== null) {
3026 taxonBase
= load(taxonBase
.getUuid());
3028 if (taxonBase
instanceof Taxon
) {
3029 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
3031 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
3032 nodes
.addAll(taxon
.getTaxonNodes());
3035 for (TaxonNode node
: nodes
) {
3036 classifications
.add(node
.getClassification());
3038 list
.addAll(classifications
);
3043 public Synonym
changeRelatedTaxonToSynonym(Taxon fromTaxon
, Taxon toTaxon
, TaxonRelationshipType oldRelationshipType
,
3044 SynonymRelationshipType synonymRelationshipType
) throws DataChangeNoRollbackException
{
3045 // Create new synonym using concept name
3046 TaxonNameBase
<?
, ?
> synonymName
= fromTaxon
.getName();
3047 Synonym synonym
= Synonym
.NewInstance(synonymName
, fromTaxon
.getSec());
3049 // Remove concept relation from taxon
3050 toTaxon
.removeTaxon(fromTaxon
, oldRelationshipType
);
3055 // Create a new synonym for the taxon
3056 SynonymRelationship synonymRelationship
;
3057 if (synonymRelationshipType
!= null
3058 && synonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF())){
3059 synonymRelationship
= toTaxon
.addHomotypicSynonym(synonym
, null, null);
3061 synonymRelationship
= toTaxon
.addHeterotypicSynonymName(synonymName
);
3064 this.saveOrUpdate(toTaxon
);
3065 //TODO: configurator and classification
3066 TaxonDeletionConfigurator config
= new TaxonDeletionConfigurator();
3067 config
.setDeleteNameIfPossible(false);
3068 this.deleteTaxon(fromTaxon
, config
, null);
3069 return synonymRelationship
.getSynonym();
3073 public DeleteResult
isDeletable(TaxonBase taxonBase
, DeleteConfiguratorBase config
){
3074 DeleteResult result
= new DeleteResult();
3075 Set
<CdmBase
> references
= commonService
.getReferencingObjectsForDeletion(taxonBase
);
3076 if (taxonBase
instanceof Taxon
){
3077 TaxonDeletionConfigurator taxonConfig
= (TaxonDeletionConfigurator
) config
;
3078 result
= isDeletableForTaxon(references
, taxonConfig
);
3080 SynonymDeletionConfigurator synonymConfig
= (SynonymDeletionConfigurator
) config
;
3081 result
= isDeletableForSynonym(references
, synonymConfig
);
3086 private DeleteResult
isDeletableForSynonym(Set
<CdmBase
> references
, SynonymDeletionConfigurator config
){
3088 DeleteResult result
= new DeleteResult();
3089 for (CdmBase ref
: references
){
3090 if (!(ref
instanceof SynonymRelationship
|| ref
instanceof Taxon
|| ref
instanceof TaxonNameBase
)){
3091 message
= "The Synonym can't be deleted as long as it is referenced by " + ref
.getClass().getSimpleName() + " with id "+ ref
.getId();
3092 result
.addException(new ReferencedObjectUndeletableException(message
));
3093 result
.addRelatedObject(ref
);
3100 private DeleteResult
isDeletableForTaxon(Set
<CdmBase
> references
, TaxonDeletionConfigurator config
){
3101 String message
= null;
3102 DeleteResult result
= new DeleteResult();
3103 for (CdmBase ref
: references
){
3104 if (!(ref
instanceof TaxonNameBase
)){
3105 if (!config
.isDeleteSynonymRelations() && (ref
instanceof SynonymRelationship
)){
3106 message
= "The Taxon can't be deleted as long as it has synonyms.";
3109 if (!config
.isDeleteDescriptions() && (ref
instanceof DescriptionBase
)){
3110 message
= "The Taxon can't be deleted as long as it has factual data.";
3114 if (!config
.isDeleteTaxonNodes() && (ref
instanceof TaxonNode
)){
3115 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3118 if (!config
.isDeleteTaxonRelationships() && (ref
instanceof TaxonNode
)){
3119 if (!config
.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship
)ref
).getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR()))){
3120 message
= "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3123 message
= "The Taxon can't be deleted as long as it belongs to a taxon node.";
3127 if (ref
instanceof PolytomousKeyNode
){
3128 message
= "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3132 if (HibernateProxyHelper
.isInstanceOf(ref
, IIdentificationKey
.class)){
3133 message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3139 /* //PolytomousKeyNode
3140 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3141 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3146 if (ref
.isInstanceOf(TaxonInteraction
.class)){
3147 message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3152 if (ref
.isInstanceOf(DeterminationEvent
.class)){
3153 message
= "Taxon can't be deleted as it is used in a determination event";
3158 if (message
!= null){
3159 result
.addException(new ReferencedObjectUndeletableException(message
));
3160 result
.addRelatedObject(ref
);
3169 public IncludedTaxaDTO
listIncludedTaxa(UUID taxonUuid
, IncludedTaxonConfiguration config
) {
3170 IncludedTaxaDTO result
= new IncludedTaxaDTO(taxonUuid
);
3172 //preliminary implementation
3174 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
3175 TaxonBase taxonBase
= find(taxonUuid
);
3176 if (taxonBase
== null){
3177 return new IncludedTaxaDTO();
3178 }else if (taxonBase
.isInstanceOf(Taxon
.class)){
3179 Taxon taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
3181 }else if (taxonBase
.isInstanceOf(Synonym
.class)){
3182 //TODO partial synonyms ??
3183 //TODO synonyms in general
3184 Synonym syn
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
3185 taxa
.addAll(syn
.getAcceptedTaxa());
3187 throw new IllegalArgumentException("Unhandled class " + taxonBase
.getClass().getSimpleName());
3190 Set
<Taxon
> related
= makeRelatedIncluded(taxa
, result
, config
);
3192 while((! related
.isEmpty()) && i
++ < 100){ //to avoid
3193 related
= makeRelatedIncluded(related
, result
, config
);
3200 * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa
3202 * @return the set of conceptually related taxa for further use
3205 * @param uncheckedTaxa
3206 * @param existingTaxa
3210 private Set
<Taxon
> makeRelatedIncluded(Set
<Taxon
> uncheckedTaxa
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3213 Set
<TaxonNode
> taxonNodes
= new HashSet
<TaxonNode
>();
3214 for (Taxon taxon
: uncheckedTaxa
){
3215 taxonNodes
.addAll(taxon
.getTaxonNodes());
3218 Set
<Taxon
> children
= new HashSet
<Taxon
>();
3219 if (! config
.onlyCongruent
){
3220 for (TaxonNode node
: taxonNodes
){
3221 List
<TaxonNode
> childNodes
= nodeService
.loadChildNodesOfTaxonNode(node
, null, true, false);
3222 for (TaxonNode child
: childNodes
){
3223 children
.add(child
.getTaxon());
3226 children
.remove(null); // just to be on the save side
3229 Iterator
<Taxon
> it
= children
.iterator();
3230 while(it
.hasNext()){
3231 UUID uuid
= it
.next().getUuid();
3232 if (existingTaxa
.contains(uuid
)){
3235 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3240 Set
<Taxon
> uncheckedAndChildren
= new HashSet
<Taxon
>(uncheckedTaxa
);
3241 uncheckedAndChildren
.addAll(children
);
3243 Set
<Taxon
> relatedTaxa
= makeConceptIncludedTaxa(uncheckedAndChildren
, existingTaxa
, config
);
3246 Set
<Taxon
> result
= new HashSet
<Taxon
>(relatedTaxa
);
3251 * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.
3252 * @return the set of these computed taxa
3254 private Set
<Taxon
> makeConceptIncludedTaxa(Set
<Taxon
> unchecked
, IncludedTaxaDTO existingTaxa
, IncludedTaxonConfiguration config
) {
3255 Set
<Taxon
> result
= new HashSet
<Taxon
>();
3257 for (Taxon taxon
: unchecked
){
3258 Set
<TaxonRelationship
> fromRelations
= taxon
.getRelationsFromThisTaxon();
3259 Set
<TaxonRelationship
> toRelations
= taxon
.getRelationsToThisTaxon();
3261 for (TaxonRelationship fromRel
: fromRelations
){
3262 if (config
.includeDoubtful
== false && fromRel
.isDoubtful()){
3265 if (fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO()) ||
3266 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.INCLUDES()) ||
3267 !config
.onlyCongruent
&& fromRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_OR_INCLUDES())
3269 result
.add(fromRel
.getToTaxon());
3273 for (TaxonRelationship toRel
: toRelations
){
3274 if (config
.includeDoubtful
== false && toRel
.isDoubtful()){
3277 if (toRel
.getType().equals(TaxonRelationshipType
.CONGRUENT_TO())){
3278 result
.add(toRel
.getFromTaxon());
3283 Iterator
<Taxon
> it
= result
.iterator();
3284 while(it
.hasNext()){
3285 UUID uuid
= it
.next().getUuid();
3286 if (existingTaxa
.contains(uuid
)){
3289 existingTaxa
.addIncludedTaxon(uuid
, new ArrayList
<UUID
>(), false);
3296 public List
<TaxonBase
> findTaxaByName(MatchingTaxonConfigurator config
){
3297 List
<TaxonBase
> taxonList
= dao
.getTaxaByName(true, false, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, 0, config
.getPropertyPath());
3302 @Transactional(readOnly
= true)
3303 public <S
extends TaxonBase
> Pager
<FindByIdentifierDTO
<S
>> findByIdentifier(
3304 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
, TaxonNode subtreeFilter
,
3305 MatchMode matchmode
, boolean includeEntity
, Integer pageSize
,
3306 Integer pageNumber
, List
<String
> propertyPaths
) {
3307 if (subtreeFilter
== null){
3308 return findByIdentifier(clazz
, identifier
, identifierType
, matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3311 Integer numberOfResults
= dao
.countByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
, matchmode
);
3312 List
<Object
[]> daoResults
= new ArrayList
<Object
[]>();
3313 if(numberOfResults
> 0) { // no point checking again
3314 daoResults
= dao
.findByIdentifier(clazz
, identifier
, identifierType
, subtreeFilter
,
3315 matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
3318 List
<FindByIdentifierDTO
<S
>> result
= new ArrayList
<FindByIdentifierDTO
<S
>>();
3319 for (Object
[] daoObj
: daoResults
){
3321 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (S
)daoObj
[2]));
3323 result
.add(new FindByIdentifierDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
3326 return new DefaultPagerImpl
<FindByIdentifierDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);