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 org
.apache
.log4j
.Logger
;
25 import org
.apache
.lucene
.index
.CorruptIndexException
;
26 import org
.apache
.lucene
.queryParser
.ParseException
;
27 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
28 import org
.apache
.lucene
.search
.BooleanFilter
;
29 import org
.apache
.lucene
.search
.BooleanQuery
;
30 import org
.apache
.lucene
.search
.DocIdSet
;
31 import org
.apache
.lucene
.search
.Query
;
32 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
33 import org
.apache
.lucene
.search
.SortField
;
34 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
35 import org
.springframework
.stereotype
.Service
;
36 import org
.springframework
.transaction
.annotation
.Transactional
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.SynonymDeletionConfigurator
;
41 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
42 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonNodeDeletionConfigurator
.ChildHandling
;
43 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
44 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
45 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
46 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
47 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
56 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
57 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
58 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
59 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
60 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
61 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
62 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
63 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
64 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
65 import eu
.etaxonomy
.cdm
.model
.common
.ITreeNode
;
66 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
67 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
68 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
69 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
70 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
71 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
72 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
73 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
74 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
75 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
76 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
77 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
78 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
79 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
80 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
81 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
82 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
83 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
84 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
85 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
86 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
87 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
88 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
89 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
90 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
91 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
92 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
93 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
94 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
95 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
96 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
97 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
98 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
99 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
100 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
101 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
102 import eu
.etaxonomy
.cdm
.model
.taxon
.ITaxonTreeNode
;
103 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
104 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
105 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
106 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
108 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
109 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
110 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
111 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
112 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
113 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
114 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
115 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
116 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
117 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
118 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
119 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
120 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
121 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
125 * @author a.kohlbecker
130 @Transactional(readOnly
= true)
131 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
132 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
134 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
136 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
138 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
142 private ITaxonNameDao nameDao
;
145 private INameService nameService
;
148 private ITaxonNodeService nodeService
;
152 private ICdmGenericDao genericDao
;
155 private IDescriptionService descriptionService
;
158 private IOrderedTermVocabularyDao orderedVocabularyDao
;
161 private IOccurrenceDao occurrenceDao
;
164 private AbstractBeanInitializer beanInitializer
;
167 private ILuceneIndexToolProvider luceneIndexToolProvider
;
172 public TaxonServiceImpl(){
173 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
177 * FIXME Candidate for harmonization
178 * rename searchByName ?
181 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
182 return dao
.getTaxaByName(name
, sec
);
186 * FIXME Candidate for harmonization
187 * list(Synonym.class, ...)
189 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
192 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
193 return dao
.getAllSynonyms(limit
, start
);
197 * FIXME Candidate for harmonization
198 * list(Taxon.class, ...)
200 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
203 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
204 return dao
.getAllTaxa(limit
, start
);
208 * FIXME Candidate for harmonization
209 * merge with getRootTaxa(Reference sec, ..., ...)
211 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
214 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
215 if (cdmFetch
== null){
216 cdmFetch
= CdmFetch
.NO_FETCH();
218 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
223 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
226 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
227 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
231 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
234 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
235 return dao
.getAllRelationships(limit
, start
);
239 * FIXME Candidate for harmonization
240 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
244 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
246 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
247 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
248 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
249 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
250 return taxonRelTypeVocabulary
;
257 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
260 @Transactional(readOnly
= false)
261 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
263 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
264 synonymName
.removeTaxonBase(synonym
);
265 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
266 taxonName
.removeTaxonBase(acceptedTaxon
);
268 synonym
.setName(taxonName
);
269 acceptedTaxon
.setName(synonymName
);
271 // the accepted taxon needs a new uuid because the concept has changed
272 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
273 //acceptedTaxon.setUuid(UUID.randomUUID());
278 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
282 @Transactional(readOnly
= false)
283 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
285 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
286 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
287 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
289 //check synonym is not homotypic
290 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
291 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
292 throw new HomotypicalGroupChangeException(message
);
295 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
297 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
298 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
300 for (Synonym heteroSynonym
: heteroSynonyms
){
301 if (synonym
.equals(heteroSynonym
)){
302 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
304 //move synonyms in same homotypic group to new accepted taxon
305 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
309 //synonym.getName().removeTaxonBase(synonym);
312 // deleteSynonym(synonym, taxon, false);
315 this.deleteSynonym(synonym
, acceptedTaxon
, new SynonymDeletionConfigurator());
317 } catch (Exception e
) {
318 logger
.info("Can't delete old synonym from database");
322 return newAcceptedTaxon
;
327 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
329 // Get name from synonym
330 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
332 // remove synonym from taxon
333 toTaxon
.removeSynonym(synonym
);
335 // Create a taxon with synonym name
336 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
338 // Add taxon relation
339 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
341 // since we are swapping names, we have to detach the name from the synonym completely.
342 // Otherwise the synonym will still be in the list of typified names.
343 synonym
.getName().removeTaxonBase(synonym
);
350 * @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)
352 @Transactional(readOnly
= false)
354 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
355 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
357 TaxonNameBase synonymName
= synonym
.getName();
358 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
362 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
363 newHomotypicalGroup
.addTypifiedName(synonymName
);
365 //remove existing basionym relationships
366 synonymName
.removeBasionyms();
368 //add basionym relationship
369 if (setBasionymRelationIfApplicable
){
370 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
371 for (TaxonNameBase basionym
: basionyms
){
372 synonymName
.addBasionym(basionym
);
376 //set synonym relationship correctly
377 // SynonymRelationship relToTaxon = null;
378 boolean relToTargetTaxonExists
= false;
379 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
380 for (SynonymRelationship rel
: existingRelations
){
381 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
382 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
383 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
384 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
385 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
386 rel
.setType(newRelationType
);
387 //TODO handle citation and microCitation
390 relToTargetTaxonExists
= true;
392 if (removeFromOtherTaxa
){
393 acceptedTaxon
.removeSynonym(synonym
, false);
399 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
400 Taxon acceptedTaxon
= targetTaxon
;
401 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
402 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
403 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
404 //TODO handle citation and microCitation
405 Reference citation
= null;
406 String microCitation
= null;
407 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
414 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
417 @Transactional(readOnly
= false)
418 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
420 clazz
= TaxonBase
.class;
422 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
427 protected void setDao(ITaxonDao dao
) {
432 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
435 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
436 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
438 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
439 if(numberOfResults
> 0) { // no point checking again
440 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
443 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
447 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
450 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
451 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
453 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
454 if(numberOfResults
> 0) { // no point checking again
455 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
462 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
465 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
466 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
468 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
469 if(numberOfResults
> 0) { // no point checking again
470 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
476 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
479 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
480 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
482 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
483 if(numberOfResults
> 0) { // no point checking again
484 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
486 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
490 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
493 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
494 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
496 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
497 if(numberOfResults
> 0) { // no point checking again
498 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
504 * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
507 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
508 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
510 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
511 if(numberOfResults
> 0) { // no point checking again
512 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
514 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
519 * @param includeRelationships
523 * @param propertyPaths
524 * @return an List which is not specifically ordered
527 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
528 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
530 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
531 relatedTaxa
.remove(taxon
);
532 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
538 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
539 * <code>taxon</code> supplied as parameter.
542 * @param includeRelationships
544 * @param maxDepth can be <code>null</code> for infinite depth
547 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
553 if(maxDepth
!= null) {
556 if(logger
.isDebugEnabled()){
557 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
559 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
560 for (TaxonRelationship taxRel
: taxonRelationships
) {
563 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
566 // filter by includeRelationships
567 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
568 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
569 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
570 if(logger
.isDebugEnabled()){
571 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
573 taxa
.add(taxRel
.getToTaxon());
574 if(maxDepth
== null || maxDepth
> 0) {
575 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
578 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
579 taxa
.add(taxRel
.getFromTaxon());
580 if(logger
.isDebugEnabled()){
581 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
583 if(maxDepth
== null || maxDepth
> 0) {
584 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
594 * @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)
597 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
598 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
600 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
601 if(numberOfResults
> 0) { // no point checking again
602 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
605 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
609 * @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)
612 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
613 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
615 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
616 if(numberOfResults
> 0) { // no point checking again
617 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
620 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
624 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
627 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
628 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
629 return t
.getHomotypicSynonymsByHomotypicGroup();
633 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
636 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
637 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
638 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
639 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
640 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
641 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
643 return heterotypicSynonymyGroups
;
647 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
649 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
650 // Class<? extends TaxonBase> clazz = null;
651 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
652 // clazz = TaxonBase.class;
653 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
654 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
655 // } else if(configurator.isDoTaxa()) {
656 // clazz = Taxon.class;
657 // //propertyPath = configurator.getTaxonPropertyPath();
658 // } else if (configurator.isDoSynonyms()) {
659 // clazz = Synonym.class;
660 // //propertyPath = configurator.getSynonymPropertyPath();
664 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
669 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
672 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
674 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
675 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
676 List
<TaxonBase
> taxa
= null;
679 long numberTaxaResults
= 0L;
682 List
<String
> propertyPath
= new ArrayList
<String
>();
683 if(configurator
.getTaxonPropertyPath() != null){
684 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
688 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
689 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
691 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
692 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
693 configurator
.getNamedAreas());
696 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
697 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
698 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
699 configurator
.getMatchMode(), configurator
.getNamedAreas(),
700 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
704 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
707 results
.addAll(taxa
);
710 numberOfResults
+= numberTaxaResults
;
712 // Names without taxa
713 if (configurator
.isDoNamesWithoutTaxa()) {
714 int numberNameResults
= 0;
716 List
<?
extends TaxonNameBase
<?
,?
>> names
=
717 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
718 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
719 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
720 if (names
.size() > 0) {
721 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
722 if (taxonName
.getTaxonBases().size() == 0) {
723 results
.add(taxonName
);
727 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
728 numberOfResults
+= numberNameResults
;
732 // Taxa from common names
734 if (configurator
.isDoTaxaByCommonNames()) {
735 taxa
= new ArrayList
<TaxonBase
>();
736 numberTaxaResults
= 0;
737 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
738 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
740 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
741 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
742 for( Object
[] entry
: commonNameResults
) {
743 taxa
.add((TaxonBase
) entry
[0]);
747 results
.addAll(taxa
);
749 numberOfResults
+= numberTaxaResults
;
753 return new DefaultPagerImpl
<IdentifiableEntity
>
754 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
757 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
758 return dao
.getUuidAndTitleCache();
762 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
765 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
766 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
767 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
768 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
769 for (TaxonDescription taxDesc
: descriptions
){
770 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
771 for (DescriptionElementBase descElem
: elements
){
772 for(Media media
: descElem
.getMedia()){
774 //find the best matching representation
775 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
784 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
787 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
788 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
793 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
796 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
797 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
798 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
800 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
801 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
803 if (limitToGalleries
== null) {
804 limitToGalleries
= false;
807 // --- resolve related taxa
808 if (includeRelationships
!= null) {
809 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
812 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
814 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
815 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
816 // --- TaxonDescriptions
817 for (Taxon t
: taxa
) {
818 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
820 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
821 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
822 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
823 for (Media media
: element
.getMedia()) {
824 taxonMedia
.add(media
);
831 if(includeOccurrences
!= null && includeOccurrences
) {
832 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
834 for (Taxon t
: taxa
) {
835 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
837 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
839 // direct media removed from specimen #3597
840 // taxonMedia.addAll(occurrence.getMedia());
842 // SpecimenDescriptions
843 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
844 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
845 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
846 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
847 for (DescriptionElementBase element
: elements
) {
848 for (Media media
: element
.getMedia()) {
849 taxonMedia
.add(media
);
856 //TODO why may collections have media attached? #
857 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
858 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
859 if (derivedUnit
.getCollection() != null){
860 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
864 // pherograms & gelPhotos
865 if (occurrence
.isInstanceOf(DnaSample
.class)) {
866 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
867 Set
<Sequence
> sequences
= dnaSample
.getSequences();
868 //we do show only those gelPhotos which lead to a consensus sequence
869 for (Sequence sequence
: sequences
) {
870 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
871 for (SingleRead singleRead
: sequence
.getSingleReads()){
872 Amplification amplification
= singleRead
.getAmplification();
873 dnaRelatedMedia
.add(amplification
.getGelPhoto());
874 dnaRelatedMedia
.add(singleRead
.getPherogram());
875 dnaRelatedMedia
.remove(null);
877 taxonMedia
.addAll(dnaRelatedMedia
);
884 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
885 // --- TaxonNameDescription
886 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
887 for (Taxon t
: taxa
) {
888 nameDescriptions
.addAll(t
.getName().getDescriptions());
890 for(TaxonNameDescription nameDescription
: nameDescriptions
){
891 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
892 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
893 for (DescriptionElementBase element
: elements
) {
894 for (Media media
: element
.getMedia()) {
895 taxonMedia
.add(media
);
902 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
907 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
910 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
911 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
915 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
918 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
919 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
923 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
926 public int countAllRelationships() {
927 return this.dao
.countAllRelationships();
934 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
937 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
938 return this.dao
.findIdenticalTaxonNames(propertyPath
);
943 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
946 public UUID
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) throws DataChangeNoRollbackException
{
948 config
= new TaxonDeletionConfigurator();
951 // --- DeleteSynonymRelations
952 if (config
.isDeleteSynonymRelations()){
953 boolean removeSynonymNameFromHomotypicalGroup
= false;
954 // use tmp Set to avoid concurrent modification
955 Set
<SynonymRelationship
> synRelsToDelete
= new HashSet
<SynonymRelationship
>();
956 synRelsToDelete
.addAll(taxon
.getSynonymRelations());
957 for (SynonymRelationship synRel
: synRelsToDelete
){
958 Synonym synonym
= synRel
.getSynonym();
959 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
960 // this will cause hibernate to delete the relationship since
961 // the SynonymRelationship field on both is annotated with removeOrphan
962 // so no further explicit deleting of the relationship should be done here
963 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
965 // --- DeleteSynonymsIfPossible
966 if (config
.isDeleteSynonymsIfPossible()){
968 boolean newHomotypicGroupIfNeeded
= true;
969 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
970 deleteSynonym(synonym
, taxon
, synConfig
);
972 // relationship will be deleted by hibernate automatically,
973 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
975 // deleteSynonymRelationships(synonym, taxon);
980 // --- DeleteTaxonRelationships
981 if (! config
.isDeleteTaxonRelationships()){
982 if (taxon
.getTaxonRelations().size() > 0){
983 String message
= "Taxon can't be deleted as it is related to another taxon. " +
984 "Remove taxon from all relations to other taxa prior to deletion.";
985 throw new ReferencedObjectUndeletableException(message
);
988 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
989 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
990 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
991 if (taxon
.equals(taxRel
.getToTaxon())){
992 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
996 taxon
.removeTaxonRelation(taxRel
);
997 /*if (taxFrom.equals(taxon)){
999 this.deleteTaxon(taxTo, taxConf, classification);
1000 } catch(DataChangeNoRollbackException e){
1001 logger.debug("A related taxon will not be deleted." + e.getMessage());
1005 this.deleteTaxon(taxFrom, taxConf, classification);
1006 } catch(DataChangeNoRollbackException e){
1007 logger.debug("A related taxon will not be deleted." + e.getMessage());
1015 if (config
.isDeleteDescriptions()){
1016 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1017 List
<TaxonDescription
> removeDescriptions
= new ArrayList
<TaxonDescription
>();
1018 for (TaxonDescription desc
: descriptions
){
1019 //TODO use description delete configurator ?
1020 //FIXME check if description is ALWAYS deletable
1021 if (desc
.getDescribedSpecimenOrObservation() != null){
1022 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1023 " which also describes specimens or abservations";
1024 throw new ReferencedObjectUndeletableException(message
);
1026 removeDescriptions
.add(desc
);
1027 descriptionService
.delete(desc
);
1030 for (TaxonDescription desc
: removeDescriptions
){
1031 taxon
.removeDescription(desc
);
1036 //check references with only reverse mapping
1037 String message
= checkForReferences(taxon
);
1038 if (message
!= null){
1039 throw new ReferencedObjectUndeletableException(message
.toString());
1042 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1043 if (taxon
.getTaxonNodes().size() > 0){
1044 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.";
1045 throw new ReferencedObjectUndeletableException(message
);
1048 if (taxon
.getTaxonNodes().size() != 0){
1049 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1050 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1051 TaxonNode node
= null;
1052 boolean deleteChildren
;
1053 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1054 deleteChildren
= true;
1056 deleteChildren
= false;
1058 boolean success
= true;
1059 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1060 while (iterator
.hasNext()){
1061 node
= iterator
.next();
1062 if (node
.getClassification().equals(classification
)){
1068 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1069 nodeService
.delete(node
);
1071 message
= "Taxon is not used in defined classification";
1072 throw new DataChangeNoRollbackException(message
);
1074 } else if (config
.isDeleteInAllClassifications()){
1075 Set
<ITaxonTreeNode
> nodesList
= new HashSet
<ITaxonTreeNode
>();
1076 nodesList
.addAll(taxon
.getTaxonNodes());
1078 for (ITaxonTreeNode treeNode
: nodesList
){
1079 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1080 if(!deleteChildren
){
1081 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1082 //nodesList.addAll(taxonNode.getChildNodes());
1083 for (Object childNode: childNodes){
1084 TaxonNode childNodeCast = (TaxonNode) childNode;
1085 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1089 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1090 deleteTaxon(childNode.getTaxon(), config, classification);
1093 // taxon.removeTaxonNode(taxonNode);
1094 //nodeService.delete(taxonNode);
1097 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1098 for (Object childNode
: childNodes
){
1099 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1100 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1103 //taxon.removeTaxonNode(taxonNode);
1106 config
.getTaxonNodeConfig().setDeleteTaxon(false);
1107 nodeService
.deleteTaxonNodes(nodesList
, config
);
1110 message
= "The taxon node could not be deleted.";
1111 throw new DataChangeNoRollbackException(message
);
1116 if (config
.isDeleteNameIfPossible()){
1119 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1120 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1121 //check whether taxon will be deleted or not
1122 if ((taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0) && name
!= null){
1123 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1124 name
.removeTaxonBase(taxon
);
1125 nameService
.save(name
);
1126 nameService
.delete(name
, config
.getNameDeletionConfig());
1128 } catch (ReferencedObjectUndeletableException e
) {
1130 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1136 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1138 for (TaxonDescription desc: descriptions){
1139 if (config.isDeleteDescriptions()){
1140 //TODO use description delete configurator ?
1141 //FIXME check if description is ALWAYS deletable
1142 taxon.removeDescription(desc);
1143 descriptionService.delete(desc);
1145 if (desc.getDescribedSpecimenOrObservations().size()>0){
1146 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1147 " which also describes specimens or observations";
1148 throw new ReferencedObjectUndeletableException(message);
1153 if (taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0){
1155 return taxon
.getUuid();
1157 message
= "Taxon can't be deleted as it is used in another Taxonnode";
1158 if (!config
.isDeleteInAllClassifications() && classification
!= null) {
1159 message
+= "The Taxonnode in " + classification
.getTitleCache() + " was deleted.";
1161 throw new ReferencedObjectUndeletableException(message
);
1167 private String
checkForReferences(Taxon taxon
){
1168 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1169 for (CdmBase referencingObject
: referencingObjects
){
1170 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1171 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1172 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";
1179 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
1180 String message
= "Taxon" + taxon
.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1185 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1186 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1190 referencingObjects
= null;
1194 @Transactional(readOnly
= false)
1195 public UUID
delete(Synonym syn
){
1196 UUID result
= syn
.getUuid();
1197 this.deleteSynonym(syn
, null);
1202 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1204 @Transactional(readOnly
= false)
1206 public void deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1207 deleteSynonym(synonym
, null, config
);
1213 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1215 @Transactional(readOnly
= false)
1217 public void deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1218 if (synonym
== null){
1221 if (config
== null){
1222 config
= new SynonymDeletionConfigurator();
1224 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1226 //remove synonymRelationship
1227 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1229 taxonSet
.add(taxon
);
1231 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1233 for (Taxon relatedTaxon
: taxonSet
){
1234 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1235 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1237 this.saveOrUpdate(synonym
);
1239 //TODO remove name from homotypical group?
1241 //remove synonym (if necessary)
1244 if (synonym
.getSynonymRelations().isEmpty()){
1245 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1246 synonym
.setName(null);
1247 dao
.delete(synonym
);
1249 //remove name if possible (and required)
1250 if (name
!= null && config
.isDeleteNameIfPossible()){
1252 nameService
.delete(name
, config
.getNameDeletionConfig());
1253 }catch (ReferencedObjectUndeletableException ex
){
1254 if (logger
.isDebugEnabled()) {
1255 logger
.debug("Name wasn't deleted as it is referenced");
1264 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1267 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1269 return this.dao
.findIdenticalNamesNew(propertyPath
);
1273 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1276 public String
getPhylumName(TaxonNameBase name
){
1277 return this.dao
.getPhylumName(name
);
1281 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1284 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1285 return dao
.deleteSynonymRelationships(syn
, taxon
);
1289 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1292 public long deleteSynonymRelationships(Synonym syn
) {
1293 return dao
.deleteSynonymRelationships(syn
, null);
1298 * @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)
1301 public List
<SynonymRelationship
> listSynonymRelationships(
1302 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1303 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1304 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1306 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1307 if(numberOfResults
> 0) { // no point checking again
1308 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1314 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1317 public Taxon
findBestMatchingTaxon(String taxonName
) {
1318 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1319 config
.setTaxonNameTitle(taxonName
);
1320 return findBestMatchingTaxon(config
);
1326 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1328 Taxon bestCandidate
= null;
1330 // 1. search for acceptet taxa
1331 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1332 boolean bestCandidateMatchesSecUuid
= false;
1333 boolean bestCandidateIsInClassification
= false;
1334 int countEqualCandidates
= 0;
1335 for(TaxonBase taxonBaseCandidate
: taxonList
){
1336 if(taxonBaseCandidate
instanceof Taxon
){
1337 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1338 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1339 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1341 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1342 bestCandidate
= newCanditate
;
1343 countEqualCandidates
= 1;
1344 bestCandidateMatchesSecUuid
= true;
1348 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1349 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1351 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1352 bestCandidate
= newCanditate
;
1353 countEqualCandidates
= 1;
1354 bestCandidateIsInClassification
= true;
1357 if (bestCandidate
== null){
1358 bestCandidate
= newCanditate
;
1359 countEqualCandidates
= 1;
1363 }else{ //not Taxon.class
1366 countEqualCandidates
++;
1369 if (bestCandidate
!= null){
1370 if(countEqualCandidates
> 1){
1371 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1372 return bestCandidate
;
1374 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1375 return bestCandidate
;
1380 // 2. search for synonyms
1381 if (config
.isIncludeSynonyms()){
1382 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1383 for(TaxonBase taxonBase
: synonymList
){
1384 if(taxonBase
instanceof Synonym
){
1385 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1386 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1387 if(!acceptetdCandidates
.isEmpty()){
1388 bestCandidate
= acceptetdCandidates
.iterator().next();
1389 if(acceptetdCandidates
.size() == 1){
1390 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1391 return bestCandidate
;
1393 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1394 return bestCandidate
;
1396 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1402 } catch (Exception e
){
1404 e
.printStackTrace();
1407 return bestCandidate
;
1410 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1411 UUID configClassificationUuid
= config
.getClassificationUuid();
1412 if (configClassificationUuid
== null){
1415 for (TaxonNode node
: taxon
.getTaxonNodes()){
1416 UUID classUuid
= node
.getClassification().getUuid();
1417 if (configClassificationUuid
.equals(classUuid
)){
1424 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1425 UUID configSecUuid
= config
.getSecUuid();
1426 if (configSecUuid
== null){
1429 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1430 return configSecUuid
.equals(taxonSecUuid
);
1434 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1437 public Synonym
findBestMatchingSynonym(String taxonName
) {
1438 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1439 if(! synonymList
.isEmpty()){
1440 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1441 if(synonymList
.size() == 1){
1442 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1445 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1454 * @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)
1457 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1458 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1460 Synonym synonym
= oldSynonymRelation
.getSynonym();
1461 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1462 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1463 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1464 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1465 //set default relationship type
1466 if (newSynonymRelationshipType
== null){
1467 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1469 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1471 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1472 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1473 boolean isSingleInGroup
= !(hgSize
> 1);
1475 if (! isSingleInGroup
){
1476 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1477 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1478 if (isHomotypicToAccepted
){
1479 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.";
1480 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1481 message
= String
.format(message
, homotypicRelatives
);
1482 throw new HomotypicalGroupChangeException(message
);
1484 if (! moveHomotypicGroup
){
1485 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.";
1486 throw new HomotypicalGroupChangeException(message
);
1489 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1491 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1493 SynonymRelationship result
= null;
1494 //move all synonyms to new taxon
1495 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1496 for (Synonym syn
: homotypicSynonyms
){
1497 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1498 for (SynonymRelationship synRelation
: synRelations
){
1499 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1500 Reference
<?
> newReference
= reference
;
1501 if (newReference
== null && keepReference
){
1502 newReference
= synRelation
.getCitation();
1504 String newRefDetail
= referenceDetail
;
1505 if (newRefDetail
== null && keepReference
){
1506 newRefDetail
= synRelation
.getCitationMicroReference();
1508 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1509 fromTaxon
.removeSynonymRelation(synRelation
, false);
1511 //change homotypic group of synonym if relType is 'homotypic'
1512 // if (newRelTypeIsHomotypic){
1513 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1516 if (synRelation
.equals(oldSynonymRelation
)){
1517 result
= newSynRelation
;
1523 saveOrUpdate(newTaxon
);
1524 //Assert that there is a result
1525 if (result
== null){
1526 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1527 throw new IllegalStateException(message
);
1533 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1536 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1537 return dao
.getUuidAndTitleCacheTaxon();
1541 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1544 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1545 return dao
.getUuidAndTitleCacheSynonym();
1549 * @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)
1552 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1553 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1554 Classification classification
, List
<Language
> languages
,
1555 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1558 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
, null);
1560 // --- execute search
1561 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1563 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1564 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1566 // --- initialize taxa, thighlight matches ....
1567 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1568 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1569 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1571 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1572 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1576 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1577 Classification classification
,
1578 Integer pageSize
, Integer pageNumber
,
1579 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1581 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1583 // --- execute search
1584 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1586 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1587 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1589 // --- initialize taxa, thighlight matches ....
1590 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1591 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1592 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1594 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1595 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1600 * @param queryString
1601 * @param classification
1603 * @param highlightFragments
1604 * @param sortFields TODO
1605 * @param directorySelectClass
1608 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1609 boolean highlightFragments
, SortField
[] sortFields
) {
1610 BooleanQuery finalQuery
= new BooleanQuery();
1611 BooleanQuery textQuery
= new BooleanQuery();
1613 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1614 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1616 if(sortFields
== null){
1617 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1619 luceneSearch
.setSortFields(sortFields
);
1621 // ---- search criteria
1622 luceneSearch
.setCdmTypRestriction(clazz
);
1624 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1625 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1627 finalQuery
.add(textQuery
, Occur
.MUST
);
1629 if(classification
!= null){
1630 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1632 luceneSearch
.setQuery(finalQuery
);
1634 if(highlightFragments
){
1635 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1637 return luceneSearch
;
1641 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1642 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1643 * drawback of requiring to do the join an indexing time.
1644 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1646 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1648 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1649 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1651 * @param queryString
1652 * @param classification
1654 * @param highlightFragments
1655 * @param sortFields TODO
1658 * @throws IOException
1660 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1661 boolean highlightFragments
, SortField
[] sortFields
) throws IOException
{
1664 String queryTermField
;
1665 String toField
= "id"; // TaxonBase.uuid
1667 if(edge
.isBidirectional()){
1668 throw new RuntimeException("Bidirectional joining not supported!");
1671 fromField
= "relatedFrom.id";
1672 queryTermField
= "relatedFrom.titleCache";
1673 } else if(edge
.isInvers()) {
1674 fromField
= "relatedTo.id";
1675 queryTermField
= "relatedTo.titleCache";
1677 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1680 BooleanQuery finalQuery
= new BooleanQuery();
1682 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1683 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1685 BooleanQuery joinFromQuery
= new BooleanQuery();
1686 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1687 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1688 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1690 if(sortFields
== null){
1691 sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1693 luceneSearch
.setSortFields(sortFields
);
1695 finalQuery
.add(joinQuery
, Occur
.MUST
);
1697 if(classification
!= null){
1698 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1700 luceneSearch
.setQuery(finalQuery
);
1702 if(highlightFragments
){
1703 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1705 return luceneSearch
;
1712 * @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)
1715 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1716 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1717 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1718 boolean highlightFragments
, Integer pageSize
,
1719 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1720 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1722 // FIXME: allow taxonomic ordering
1723 // 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";
1724 // this require building a special sort column by a special classBridge
1725 if(highlightFragments
){
1726 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1727 "currently not fully supported by this method and thus " +
1728 "may not work with common names and misapplied names.");
1731 // convert sets to lists
1732 List
<NamedArea
> namedAreaList
= null;
1733 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1734 if(namedAreas
!= null){
1735 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1736 namedAreaList
.addAll(namedAreas
);
1738 if(distributionStatus
!= null){
1739 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1740 distributionStatusList
.addAll(distributionStatus
);
1743 // set default if parameter is null
1744 if(searchModes
== null){
1745 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1748 // set sort order and thus override any sort orders which may have been
1749 // defindes by prepare*Search methods
1750 if(orderHints
== null){
1751 orderHints
= OrderHint
.NOMENCLATURAL_SORT_ORDER
;
1753 SortField
[] sortFields
= new SortField
[orderHints
.size()];
1755 for(OrderHint oh
: orderHints
){
1756 sortFields
[i
++] = oh
.toSortField();
1758 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1759 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1762 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1764 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1765 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1768 ======== filtering by distribution , HOWTO ========
1770 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1771 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1772 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1773 which will be put into a FilteredQuersy in the end ?
1776 3. how does it work in spatial?
1778 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1779 - http://www.infoq.com/articles/LuceneSpatialSupport
1780 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1781 ------------------------------------------------------------------------
1784 A) use a separate distribution filter per index sub-query/search:
1785 - byTaxonSyonym (query TaxaonBase):
1786 use a join area filter (Distribution -> TaxonBase)
1787 - byCommonName (query DescriptionElementBase): use an area filter on
1788 DescriptionElementBase !!! PROBLEM !!!
1789 This cannot work since the distributions are different entities than the
1790 common names and thus these are different lucene documents.
1791 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1792 use a join area filter (Distribution -> TaxonBase)
1794 B) use a common distribution filter for all index sub-query/searches:
1795 - use a common join area filter (Distribution -> TaxonBase)
1796 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1797 PROBLEM in this case: we are losing the fragment highlighting for the
1798 common names, since the returned documents are always TaxonBases
1801 /* The QueryFactory for creating filter queries on Distributions should
1802 * The query factory used for the common names query cannot be reused
1803 * for this case, since we want to only record the text fields which are
1804 * actually used in the primary query
1806 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1808 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1811 // search for taxa or synonyms
1812 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1813 Class taxonBaseSubclass
= TaxonBase
.class;
1814 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1815 taxonBaseSubclass
= Taxon
.class;
1816 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1817 taxonBaseSubclass
= Synonym
.class;
1819 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
, sortFields
));
1820 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1821 /* A) does not work!!!!
1822 if(addDistributionFilter){
1823 // in this case we need a filter which uses a join query
1824 // to get the TaxonBase documents for the DescriptionElementBase documents
1825 // which are matching the areas in question
1826 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1828 distributionStatusList,
1829 distributionFilterQueryFactory
1831 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1834 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1835 // add additional area filter for synonyms
1836 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1837 String toField
= "accTaxon.id"; // id in TaxonBase index
1839 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1841 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1842 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1847 // search by CommonTaxonName
1848 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1850 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1851 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1852 "inDescription.taxon.id",
1854 QueryFactory
.addTypeRestriction(
1855 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1856 , CommonTaxonName
.class
1858 CommonTaxonName
.class);
1859 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1860 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1861 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1862 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1863 byCommonNameSearch
.setSortFields(sortFields
);
1864 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1866 luceneSearches
.add(byCommonNameSearch
);
1868 /* A) does not work!!!!
1870 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1871 queryString, classification, null, languages, highlightFragments)
1873 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1874 if(addDistributionFilter){
1875 // in this case we are able to use DescriptionElementBase documents
1876 // which are matching the areas in question directly
1877 BooleanQuery byDistributionQuery = createByDistributionQuery(
1879 distributionStatusList,
1880 distributionFilterQueryFactory
1882 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1886 // search by misapplied names
1887 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1889 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1890 // which allows doing query time joins
1891 // finds the misapplied name (Taxon B) which is an misapplication for
1892 // a related Taxon A.
1894 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1895 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1896 queryString
, classification
, languages
, highlightFragments
, sortFields
));
1897 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1899 if(addDistributionFilter
){
1900 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1903 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1904 * Maybe this is a but in java itself java.
1906 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
1909 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
1911 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
1912 * will execute as expected:
1914 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1915 * String toField = "relation." + misappliedNameForUuid +".to.id";
1917 * Comparing both strings by the String.equals method returns true, so both String are identical.
1919 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
1920 * 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)
1921 * The bug is persistent after a reboot of the development computer.
1923 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1924 // String toField = "relation." + misappliedNameForUuid +".to.id";
1925 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
1926 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
1927 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
1929 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1930 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1931 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
1933 // debug code for bug described above
1934 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
1935 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
1937 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
1941 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
1942 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1945 if(addDistributionFilter
){
1948 // in this case we need a filter which uses a join query
1949 // to get the TaxonBase documents for the DescriptionElementBase documents
1950 // which are matching the areas in question
1952 // for toTaxa, doByCommonName
1953 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
1955 distributionStatusList
,
1956 distributionFilterQueryFactory
1958 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1961 if (addDistributionFilter
){
1962 multiSearch
.setFilter(multiIndexByAreaFilter
);
1966 // --- execute search
1967 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1969 // --- initialize taxa, highlight matches ....
1970 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1973 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1974 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1976 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1977 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1981 * @param namedAreaList at least one area must be in the list
1982 * @param distributionStatusList optional
1984 * @throws IOException
1986 protected Query
createByDistributionJoinQuery(
1987 List
<NamedArea
> namedAreaList
,
1988 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1989 QueryFactory queryFactory
1990 ) throws IOException
{
1992 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1993 String toField
= "id"; // id in TaxonBase index
1995 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
1997 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1999 return taxonAreaJoinQuery
;
2003 * @param namedAreaList
2004 * @param distributionStatusList
2005 * @param queryFactory
2008 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
2009 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
2010 BooleanQuery areaQuery
= new BooleanQuery();
2011 // area field from Distribution
2012 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
2014 // status field from Distribution
2015 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
2016 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
2019 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
2024 * This method has been primarily created for testing the area join query but might
2025 * also be useful in other situations
2027 * @param namedAreaList
2028 * @param distributionStatusList
2029 * @param classification
2030 * @param highlightFragments
2032 * @throws IOException
2034 protected LuceneSearch
prepareByDistributionSearch(
2035 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2036 Classification classification
) throws IOException
{
2038 BooleanQuery finalQuery
= new BooleanQuery();
2040 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2042 // FIXME is this query factory using the wrong type?
2043 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2045 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2046 luceneSearch
.setSortFields(sortFields
);
2049 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2051 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2053 if(classification
!= null){
2054 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2057 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2058 luceneSearch
.setQuery(finalQuery
);
2060 return luceneSearch
;
2066 * @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)
2069 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2070 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2071 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2072 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2075 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2077 // --- execute search
2078 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2080 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2081 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2083 // --- initialize taxa, highlight matches ....
2084 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2085 @SuppressWarnings("rawtypes")
2086 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2087 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2089 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2090 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2096 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2097 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2098 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2100 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2101 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
, null);
2103 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2105 // --- execute search
2106 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2108 // --- initialize taxa, highlight matches ....
2109 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2111 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2112 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2113 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2115 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2116 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2118 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2119 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2126 * @param queryString
2127 * @param classification
2130 * @param highlightFragments
2131 * @param directorySelectClass
2134 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2135 String queryString
, Classification classification
, List
<Feature
> features
,
2136 List
<Language
> languages
, boolean highlightFragments
) {
2138 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2139 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2141 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2143 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2144 languages
, descriptionElementQueryFactory
);
2146 luceneSearch
.setSortFields(sortFields
);
2147 luceneSearch
.setCdmTypRestriction(clazz
);
2148 luceneSearch
.setQuery(finalQuery
);
2149 if(highlightFragments
){
2150 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2153 return luceneSearch
;
2157 * @param queryString
2158 * @param classification
2161 * @param descriptionElementQueryFactory
2164 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2165 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2166 BooleanQuery finalQuery
= new BooleanQuery();
2167 BooleanQuery textQuery
= new BooleanQuery();
2168 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2172 if(languages
== null || languages
.size() == 0){
2173 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2175 nameQuery
= new BooleanQuery();
2176 BooleanQuery languageSubQuery
= new BooleanQuery();
2177 for(Language lang
: languages
){
2178 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2180 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2181 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2183 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2186 // text field from TextData
2187 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2189 // --- TermBase fields - by representation ----
2190 // state field from CategoricalData
2191 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2193 // state field from CategoricalData
2194 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2196 // area field from Distribution
2197 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2199 // status field from Distribution
2200 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2202 finalQuery
.add(textQuery
, Occur
.MUST
);
2203 // --- classification ----
2205 if(classification
!= null){
2206 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2209 // --- IdentifieableEntity fields - by uuid
2210 if(features
!= null && features
.size() > 0 ){
2211 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2214 // the description must be associated with a taxon
2215 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2217 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2222 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2223 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2224 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2226 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2227 * or {@link MultilanguageTextFieldBridge }
2228 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2229 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2230 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2232 * TODO move to utiliy class !!!!!!!!
2234 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2236 if(stringBuilder
== null){
2237 stringBuilder
= new StringBuilder();
2239 if(languages
== null || languages
.size() == 0){
2240 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2242 for(Language lang
: languages
){
2243 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2246 return stringBuilder
;
2250 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2251 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2252 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2254 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2257 UUID nameUuid
= taxon
.getName().getUuid();
2258 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2259 String epithetOfTaxon
= null;
2260 String infragenericEpithetOfTaxon
= null;
2261 String infraspecificEpithetOfTaxon
= null;
2262 if (taxonName
.isSpecies()){
2263 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2264 } else if (taxonName
.isInfraGeneric()){
2265 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2266 } else if (taxonName
.isInfraSpecific()){
2267 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2269 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2270 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2271 List
<String
> taxonNames
= new ArrayList
<String
>();
2273 for (TaxonNode node
: nodes
){
2274 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2275 // List<String> synonymsEpithet = new ArrayList<String>();
2277 if (node
.getClassification().equals(classification
)){
2278 if (!node
.isTopmostNode()){
2279 TaxonNode parent
= node
.getParent();
2280 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2281 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2282 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2283 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2284 Rank rankOfTaxon
= taxonName
.getRank();
2287 //create inferred synonyms for species, subspecies
2288 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2290 Synonym inferredEpithet
= null;
2291 Synonym inferredGenus
= null;
2292 Synonym potentialCombination
= null;
2294 List
<String
> propertyPaths
= new ArrayList
<String
>();
2295 propertyPaths
.add("synonym");
2296 propertyPaths
.add("synonym.name");
2297 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2298 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2300 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2301 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2303 List
<TaxonRelationship
> taxonRelListParent
= null;
2304 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2305 if (doWithMisappliedNames
){
2306 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2307 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2311 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2314 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2315 Synonym syn
= synonymRelationOfParent
.getSynonym();
2317 inferredEpithet
= createInferredEpithets(taxon
,
2318 zooHashMap
, taxonName
, epithetOfTaxon
,
2319 infragenericEpithetOfTaxon
,
2320 infraspecificEpithetOfTaxon
,
2321 taxonNames
, parentName
,
2325 inferredSynonyms
.add(inferredEpithet
);
2326 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2327 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2330 if (doWithMisappliedNames
){
2332 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2333 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2335 inferredEpithet
= createInferredEpithets(taxon
,
2336 zooHashMap
, taxonName
, epithetOfTaxon
,
2337 infragenericEpithetOfTaxon
,
2338 infraspecificEpithetOfTaxon
,
2339 taxonNames
, parentName
,
2342 inferredSynonyms
.add(inferredEpithet
);
2343 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2344 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2348 if (!taxonNames
.isEmpty()){
2349 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2350 ZoologicalName name
;
2351 if (!synNotInCDM
.isEmpty()){
2352 inferredSynonymsToBeRemoved
.clear();
2354 for (Synonym syn
:inferredSynonyms
){
2355 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2356 if (!synNotInCDM
.contains(name
.getNameCache())){
2357 inferredSynonymsToBeRemoved
.add(syn
);
2361 // Remove identified Synonyms from inferredSynonyms
2362 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2363 inferredSynonyms
.remove(synonym
);
2368 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2371 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2372 TaxonNameBase synName
;
2373 ZoologicalName inferredSynName
;
2375 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2376 inferredGenus
= createInferredGenus(taxon
,
2377 zooHashMap
, taxonName
, epithetOfTaxon
,
2378 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2380 inferredSynonyms
.add(inferredGenus
);
2381 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2382 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2387 if (doWithMisappliedNames
){
2389 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2390 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2391 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2393 inferredSynonyms
.add(inferredGenus
);
2394 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2395 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2400 if (!taxonNames
.isEmpty()){
2401 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2402 ZoologicalName name
;
2403 if (!synNotInCDM
.isEmpty()){
2404 inferredSynonymsToBeRemoved
.clear();
2406 for (Synonym syn
:inferredSynonyms
){
2407 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2408 if (!synNotInCDM
.contains(name
.getNameCache())){
2409 inferredSynonymsToBeRemoved
.add(syn
);
2413 // Remove identified Synonyms from inferredSynonyms
2414 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2415 inferredSynonyms
.remove(synonym
);
2420 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2422 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2423 ZoologicalName inferredSynName
;
2424 //for all synonyms of the parent...
2425 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2426 TaxonNameBase synName
;
2427 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2428 synName
= synParent
.getName();
2430 HibernateProxyHelper
.deproxy(synParent
);
2432 // Set the sourceReference
2433 sourceReference
= synParent
.getSec();
2435 // Determine the idInSource
2436 String idInSourceParent
= getIdInSource(synParent
);
2438 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2439 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2440 String synParentInfragenericName
= null;
2441 String synParentSpecificEpithet
= null;
2443 if (parentSynZooName
.isInfraGeneric()){
2444 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2446 if (parentSynZooName
.isSpecies()){
2447 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2450 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2451 synonymsGenus.put(synGenusName, idInSource);
2454 //for all synonyms of the taxon
2456 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2458 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2459 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2460 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2462 synParentInfragenericName
,
2463 synParentSpecificEpithet
, syn
, zooHashMap
);
2465 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2466 inferredSynonyms
.add(potentialCombination
);
2467 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2468 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2475 if (doWithMisappliedNames
){
2477 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2479 TaxonNameBase misappliedParentName
;
2481 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2482 misappliedParentName
= misappliedParent
.getName();
2484 HibernateProxyHelper
.deproxy(misappliedParent
);
2486 // Set the sourceReference
2487 sourceReference
= misappliedParent
.getSec();
2489 // Determine the idInSource
2490 String idInSourceParent
= getIdInSource(misappliedParent
);
2492 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2493 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2494 String synParentInfragenericName
= null;
2495 String synParentSpecificEpithet
= null;
2497 if (parentSynZooName
.isInfraGeneric()){
2498 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2500 if (parentSynZooName
.isSpecies()){
2501 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2505 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2506 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2507 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2508 potentialCombination
= createPotentialCombination(
2509 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2511 synParentInfragenericName
,
2512 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2515 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2516 inferredSynonyms
.add(potentialCombination
);
2517 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2518 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2523 if (!taxonNames
.isEmpty()){
2524 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2525 ZoologicalName name
;
2526 if (!synNotInCDM
.isEmpty()){
2527 inferredSynonymsToBeRemoved
.clear();
2528 for (Synonym syn
:inferredSynonyms
){
2530 name
= (ZoologicalName
) syn
.getName();
2531 }catch (ClassCastException e
){
2532 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2534 if (!synNotInCDM
.contains(name
.getNameCache())){
2535 inferredSynonymsToBeRemoved
.add(syn
);
2538 // Remove identified Synonyms from inferredSynonyms
2539 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2540 inferredSynonyms
.remove(synonym
);
2546 logger
.info("The synonymrelationship type is not defined.");
2547 return inferredSynonyms
;
2554 return inferredSynonyms
;
2557 private Synonym
createPotentialCombination(String idInSourceParent
,
2558 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2559 String synParentInfragenericName
, String synParentSpecificEpithet
,
2560 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2561 Synonym potentialCombination
;
2562 Reference sourceReference
;
2563 ZoologicalName inferredSynName
;
2564 HibernateProxyHelper
.deproxy(syn
);
2566 // Set sourceReference
2567 sourceReference
= syn
.getSec();
2568 if (sourceReference
== null){
2569 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2571 if (!parentSynZooName
.getTaxa().isEmpty()){
2572 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2574 sourceReference
= taxon
.getSec();
2577 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2579 String synTaxonInfraSpecificName
= null;
2581 if (parentSynZooName
.isSpecies()){
2582 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2585 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2586 synonymsEpithet.add(epithetName);
2589 //create potential combinations...
2590 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2592 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2593 if (zooSynName
.isSpecies()){
2594 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2595 if (parentSynZooName
.isInfraGeneric()){
2596 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2599 if (zooSynName
.isInfraSpecific()){
2600 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2601 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2603 if (parentSynZooName
.isInfraGeneric()){
2604 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2608 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2610 // Set the sourceReference
2611 potentialCombination
.setSec(sourceReference
);
2614 // Determine the idInSource
2615 String idInSourceSyn
= getIdInSource(syn
);
2617 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2618 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2619 inferredSynName
.addSource(originalSource
);
2620 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2621 potentialCombination
.addSource(originalSource
);
2624 inferredSynName
.generateTitle();
2626 return potentialCombination
;
2629 private Synonym
createInferredGenus(Taxon taxon
,
2630 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2631 String epithetOfTaxon
, String genusOfTaxon
,
2632 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2635 Synonym inferredGenus
;
2636 TaxonNameBase synName
;
2637 ZoologicalName inferredSynName
;
2638 synName
=syn
.getName();
2639 HibernateProxyHelper
.deproxy(syn
);
2641 // Determine the idInSource
2642 String idInSourceSyn
= getIdInSource(syn
);
2643 String idInSourceTaxon
= getIdInSource(taxon
);
2644 // Determine the sourceReference
2645 Reference sourceReference
= syn
.getSec();
2647 //logger.warn(sourceReference.getTitleCache());
2649 synName
= syn
.getName();
2650 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2651 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2652 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2653 synonymsEpithet.add(synSpeciesEpithetName);
2656 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2657 //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...
2660 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2661 if (zooParentName
.isInfraGeneric()){
2662 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2665 if (taxonName
.isSpecies()){
2666 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2668 if (taxonName
.isInfraSpecific()){
2669 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2670 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2674 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2676 // Set the sourceReference
2677 inferredGenus
.setSec(sourceReference
);
2679 // Add the original source
2680 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2681 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2682 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2683 inferredGenus
.addSource(originalSource
);
2685 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2686 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2687 inferredSynName
.addSource(originalSource
);
2688 originalSource
= null;
2691 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2692 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2693 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2694 inferredGenus
.addSource(originalSource
);
2696 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2697 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2698 inferredSynName
.addSource(originalSource
);
2699 originalSource
= null;
2702 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2704 inferredSynName
.generateTitle();
2707 return inferredGenus
;
2710 private Synonym
createInferredEpithets(Taxon taxon
,
2711 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2712 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2713 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2714 TaxonNameBase parentName
, TaxonBase syn
) {
2716 Synonym inferredEpithet
;
2717 TaxonNameBase
<?
,?
> synName
;
2718 ZoologicalName inferredSynName
;
2719 HibernateProxyHelper
.deproxy(syn
);
2721 // Determine the idInSource
2722 String idInSourceSyn
= getIdInSource(syn
);
2723 String idInSourceTaxon
= getIdInSource(taxon
);
2724 // Determine the sourceReference
2725 Reference
<?
> sourceReference
= syn
.getSec();
2727 if (sourceReference
== null){
2728 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2729 sourceReference
= taxon
.getSec();
2732 synName
= syn
.getName();
2733 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2734 String synGenusName
= zooSynName
.getGenusOrUninomial();
2735 String synInfraGenericEpithet
= null;
2736 String synSpecificEpithet
= null;
2738 if (zooSynName
.getInfraGenericEpithet() != null){
2739 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2742 if (zooSynName
.isInfraSpecific()){
2743 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2746 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2747 synonymsGenus.put(synGenusName, idInSource);
2750 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2752 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2753 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2754 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2756 inferredSynName
.setGenusOrUninomial(synGenusName
);
2758 if (parentName
.isInfraGeneric()){
2759 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2761 if (taxonName
.isSpecies()){
2762 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2763 }else if (taxonName
.isInfraSpecific()){
2764 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2765 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2768 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2770 // Set the sourceReference
2771 inferredEpithet
.setSec(sourceReference
);
2773 /* Add the original source
2774 if (idInSource != null) {
2775 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2778 Reference citation = getCitation(syn);
2779 if (citation != null) {
2780 originalSource.setCitation(citation);
2781 inferredEpithet.addSource(originalSource);
2784 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2787 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2788 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2790 inferredEpithet
.addSource(originalSource
);
2792 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2793 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2795 inferredSynName
.addSource(originalSource
);
2799 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2801 inferredSynName
.generateTitle();
2802 return inferredEpithet
;
2806 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2807 * Very likely only useful for createInferredSynonyms().
2812 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2813 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2814 if (taxonName
== null) {
2815 taxonName
= zooHashMap
.get(uuid
);
2821 * Returns the idInSource for a given Synonym.
2824 private String
getIdInSource(TaxonBase taxonBase
) {
2825 String idInSource
= null;
2826 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2827 if (sources
.size() == 1) {
2828 IdentifiableSource source
= sources
.iterator().next();
2829 if (source
!= null) {
2830 idInSource
= source
.getIdInSource();
2832 } else if (sources
.size() > 1) {
2835 for (IdentifiableSource source
: sources
) {
2836 idInSource
+= source
.getIdInSource();
2837 if (count
< sources
.size()) {
2842 } else if (sources
.size() == 0){
2843 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2852 * Returns the citation for a given Synonym.
2855 private Reference
getCitation(Synonym syn
) {
2856 Reference citation
= null;
2857 Set
<IdentifiableSource
> sources
= syn
.getSources();
2858 if (sources
.size() == 1) {
2859 IdentifiableSource source
= sources
.iterator().next();
2860 if (source
!= null) {
2861 citation
= source
.getCitation();
2863 } else if (sources
.size() > 1) {
2864 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2871 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2872 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2874 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2875 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2876 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2878 return inferredSynonyms
;
2882 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2885 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2887 // TODO quickly implemented, create according dao !!!!
2888 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2889 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2890 List
<Classification
> list
= new ArrayList
<Classification
>();
2892 if (taxonBase
== null) {
2896 taxonBase
= load(taxonBase
.getUuid());
2898 if (taxonBase
instanceof Taxon
) {
2899 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2901 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2902 nodes
.addAll(taxon
.getTaxonNodes());
2905 for (TaxonNode node
: nodes
) {
2906 classifications
.add(node
.getClassification());
2908 list
.addAll(classifications
);