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
.Marker
;
70 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
71 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
72 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
73 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
74 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
75 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
76 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
77 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
78 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
79 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
80 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
81 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
82 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
83 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
84 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
85 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
86 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
87 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
88 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
89 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
90 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
91 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
92 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
93 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
94 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
95 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
96 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
97 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
98 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
99 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
100 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
101 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
102 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
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();
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 descriptionService
.delete(desc
);
1027 taxon
.removeDescription(desc
);
1032 //check references with only reverse mapping
1033 String message
= checkForReferences(taxon
);
1034 if (message
!= null){
1035 throw new ReferencedObjectUndeletableException(message
.toString());
1038 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1039 if (taxon
.getTaxonNodes().size() > 0){
1040 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.";
1041 throw new ReferencedObjectUndeletableException(message
);
1044 if (taxon
.getTaxonNodes().size() != 0){
1045 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1046 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1047 TaxonNode node
= null;
1048 boolean deleteChildren
;
1049 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1050 deleteChildren
= true;
1052 deleteChildren
= false;
1054 boolean success
= true;
1055 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1056 while (iterator
.hasNext()){
1057 node
= iterator
.next();
1058 if (node
.getClassification().equals(classification
)){
1064 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1065 nodeService
.delete(node
);
1067 message
= "Taxon is not used in defined classification";
1068 throw new DataChangeNoRollbackException(message
);
1070 } else if (config
.isDeleteInAllClassifications()){
1071 Set
<ITreeNode
> nodesList
= new HashSet
<ITreeNode
>();
1072 nodesList
.addAll(taxon
.getTaxonNodes());
1074 for (ITreeNode treeNode
: nodesList
){
1075 TaxonNode taxonNode
= (TaxonNode
) treeNode
;
1077 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1078 for (Object childNode
: childNodes
){
1079 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1080 deleteTaxon(childNodeCast
.getTaxon(), config
, classification
);
1084 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1085 deleteTaxon(childNode.getTaxon(), config, classification);
1088 //taxon.removeTaxonNode(taxonNode);
1090 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1091 for (Object childNode
: childNodes
){
1092 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1093 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1096 //taxon.removeTaxonNode(taxonNode);
1100 nodeService
.deleteTaxonNodes(nodesList
, config
);
1103 message
= "The taxon node could not be deleted.";
1104 throw new DataChangeNoRollbackException(message
);
1109 if (config
.isDeleteNameIfPossible()){
1112 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1113 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1114 //check whether taxon will be deleted or not
1115 if (taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0){
1116 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1117 name
.removeTaxonBase(taxon
);
1118 nameService
.save(name
);
1119 nameService
.delete(name
, config
.getNameDeletionConfig());
1121 } catch (ReferencedObjectUndeletableException e
) {
1123 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1129 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1131 for (TaxonDescription desc: descriptions){
1132 if (config.isDeleteDescriptions()){
1133 //TODO use description delete configurator ?
1134 //FIXME check if description is ALWAYS deletable
1135 taxon.removeDescription(desc);
1136 descriptionService.delete(desc);
1138 if (desc.getDescribedSpecimenOrObservations().size()>0){
1139 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1140 " which also describes specimens or observations";
1141 throw new ReferencedObjectUndeletableException(message);
1146 if (taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0){
1148 return taxon
.getUuid();
1150 message
= "Taxon can't be deleted as it is used in another Taxonnode";
1151 if (!config
.isDeleteInAllClassifications() && classification
!= null) {
1152 message
+= "The Taxonnode in " + classification
.getTitleCache() + " was deleted.";
1154 throw new ReferencedObjectUndeletableException(message
);
1160 private String
checkForReferences(Taxon taxon
){
1161 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1162 for (CdmBase referencingObject
: referencingObjects
){
1163 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1164 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1165 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";
1172 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
1173 String message
= "Taxon" + taxon
.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1178 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1179 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1183 referencingObjects
= null;
1187 @Transactional(readOnly
= false)
1188 public UUID
delete(Synonym syn
){
1189 UUID result
= syn
.getUuid();
1190 this.deleteSynonym(syn
, null);
1195 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1197 @Transactional(readOnly
= false)
1199 public void deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1200 deleteSynonym(synonym
, null, config
);
1206 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1208 @Transactional(readOnly
= false)
1210 public void deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1211 if (synonym
== null){
1214 if (config
== null){
1215 config
= new SynonymDeletionConfigurator();
1217 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1219 //remove synonymRelationship
1220 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1222 taxonSet
.add(taxon
);
1224 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1226 for (Taxon relatedTaxon
: taxonSet
){
1227 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1228 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1230 this.saveOrUpdate(synonym
);
1232 //TODO remove name from homotypical group?
1234 //remove synonym (if necessary)
1237 if (synonym
.getSynonymRelations().isEmpty()){
1238 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1239 synonym
.setName(null);
1240 dao
.delete(synonym
);
1242 //remove name if possible (and required)
1243 if (name
!= null && config
.isDeleteNameIfPossible()){
1245 nameService
.delete(name
, config
.getNameDeletionConfig());
1246 }catch (ReferencedObjectUndeletableException ex
){
1247 if (logger
.isDebugEnabled()) {
1248 logger
.debug("Name wasn't deleted as it is referenced");
1257 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1260 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1262 return this.dao
.findIdenticalNamesNew(propertyPath
);
1266 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1269 public String
getPhylumName(TaxonNameBase name
){
1270 return this.dao
.getPhylumName(name
);
1274 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1277 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1278 return dao
.deleteSynonymRelationships(syn
, taxon
);
1282 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1285 public long deleteSynonymRelationships(Synonym syn
) {
1286 return dao
.deleteSynonymRelationships(syn
, null);
1291 * @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)
1294 public List
<SynonymRelationship
> listSynonymRelationships(
1295 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1296 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1297 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1299 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1300 if(numberOfResults
> 0) { // no point checking again
1301 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1307 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1310 public Taxon
findBestMatchingTaxon(String taxonName
) {
1311 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1312 config
.setTaxonNameTitle(taxonName
);
1313 return findBestMatchingTaxon(config
);
1319 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1321 Taxon bestCandidate
= null;
1323 // 1. search for acceptet taxa
1324 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1325 boolean bestCandidateMatchesSecUuid
= false;
1326 boolean bestCandidateIsInClassification
= false;
1327 int countEqualCandidates
= 0;
1328 for(TaxonBase taxonBaseCandidate
: taxonList
){
1329 if(taxonBaseCandidate
instanceof Taxon
){
1330 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1331 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1332 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1334 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1335 bestCandidate
= newCanditate
;
1336 countEqualCandidates
= 1;
1337 bestCandidateMatchesSecUuid
= true;
1341 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1342 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1344 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1345 bestCandidate
= newCanditate
;
1346 countEqualCandidates
= 1;
1347 bestCandidateIsInClassification
= true;
1350 if (bestCandidate
== null){
1351 bestCandidate
= newCanditate
;
1352 countEqualCandidates
= 1;
1356 }else{ //not Taxon.class
1359 countEqualCandidates
++;
1362 if (bestCandidate
!= null){
1363 if(countEqualCandidates
> 1){
1364 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1365 return bestCandidate
;
1367 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1368 return bestCandidate
;
1373 // 2. search for synonyms
1374 if (config
.isIncludeSynonyms()){
1375 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1376 for(TaxonBase taxonBase
: synonymList
){
1377 if(taxonBase
instanceof Synonym
){
1378 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1379 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1380 if(!acceptetdCandidates
.isEmpty()){
1381 bestCandidate
= acceptetdCandidates
.iterator().next();
1382 if(acceptetdCandidates
.size() == 1){
1383 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1384 return bestCandidate
;
1386 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1387 return bestCandidate
;
1389 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1395 } catch (Exception e
){
1397 e
.printStackTrace();
1400 return bestCandidate
;
1403 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1404 UUID configClassificationUuid
= config
.getClassificationUuid();
1405 if (configClassificationUuid
== null){
1408 for (TaxonNode node
: taxon
.getTaxonNodes()){
1409 UUID classUuid
= node
.getClassification().getUuid();
1410 if (configClassificationUuid
.equals(classUuid
)){
1417 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1418 UUID configSecUuid
= config
.getSecUuid();
1419 if (configSecUuid
== null){
1422 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1423 return configSecUuid
.equals(taxonSecUuid
);
1427 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1430 public Synonym
findBestMatchingSynonym(String taxonName
) {
1431 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1432 if(! synonymList
.isEmpty()){
1433 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1434 if(synonymList
.size() == 1){
1435 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1438 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1447 * @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)
1450 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1451 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1453 Synonym synonym
= oldSynonymRelation
.getSynonym();
1454 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1455 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1456 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1457 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1458 //set default relationship type
1459 if (newSynonymRelationshipType
== null){
1460 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1462 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1464 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1465 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1466 boolean isSingleInGroup
= !(hgSize
> 1);
1468 if (! isSingleInGroup
){
1469 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1470 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1471 if (isHomotypicToAccepted
){
1472 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.";
1473 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1474 message
= String
.format(message
, homotypicRelatives
);
1475 throw new HomotypicalGroupChangeException(message
);
1477 if (! moveHomotypicGroup
){
1478 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.";
1479 throw new HomotypicalGroupChangeException(message
);
1482 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1484 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1486 SynonymRelationship result
= null;
1487 //move all synonyms to new taxon
1488 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1489 for (Synonym syn
: homotypicSynonyms
){
1490 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1491 for (SynonymRelationship synRelation
: synRelations
){
1492 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1493 Reference
<?
> newReference
= reference
;
1494 if (newReference
== null && keepReference
){
1495 newReference
= synRelation
.getCitation();
1497 String newRefDetail
= referenceDetail
;
1498 if (newRefDetail
== null && keepReference
){
1499 newRefDetail
= synRelation
.getCitationMicroReference();
1501 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1502 fromTaxon
.removeSynonymRelation(synRelation
, false);
1504 //change homotypic group of synonym if relType is 'homotypic'
1505 // if (newRelTypeIsHomotypic){
1506 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1509 if (synRelation
.equals(oldSynonymRelation
)){
1510 result
= newSynRelation
;
1516 saveOrUpdate(newTaxon
);
1517 //Assert that there is a result
1518 if (result
== null){
1519 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1520 throw new IllegalStateException(message
);
1526 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1529 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1530 return dao
.getUuidAndTitleCacheTaxon();
1534 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1537 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1538 return dao
.getUuidAndTitleCacheSynonym();
1542 * @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)
1545 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1546 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1547 Classification classification
, List
<Language
> languages
,
1548 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1551 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
);
1553 // --- execute search
1554 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1556 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1557 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1559 // --- initialize taxa, thighlight matches ....
1560 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1561 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1562 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1564 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1565 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1569 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1570 Classification classification
,
1571 Integer pageSize
, Integer pageNumber
,
1572 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1574 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1576 // --- execute search
1577 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1579 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1580 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1582 // --- initialize taxa, thighlight matches ....
1583 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1584 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1585 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1587 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1588 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1593 * @param queryString
1594 * @param classification
1596 * @param highlightFragments
1597 * @param directorySelectClass
1600 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1601 boolean highlightFragments
) {
1602 BooleanQuery finalQuery
= new BooleanQuery();
1603 BooleanQuery textQuery
= new BooleanQuery();
1605 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1606 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1608 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1609 luceneSearch
.setSortFields(sortFields
);
1611 // ---- search criteria
1612 luceneSearch
.setCdmTypRestriction(clazz
);
1614 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1615 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1617 finalQuery
.add(textQuery
, Occur
.MUST
);
1619 if(classification
!= null){
1620 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1622 luceneSearch
.setQuery(finalQuery
);
1624 if(highlightFragments
){
1625 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1627 return luceneSearch
;
1631 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1632 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1633 * drawback of requiring to do the join an indexing time.
1634 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1636 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1638 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1639 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1642 * @param queryString
1643 * @param classification
1645 * @param highlightFragments
1647 * @throws IOException
1649 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1650 boolean highlightFragments
) throws IOException
{
1653 String queryTermField
;
1654 String toField
= "id"; // TaxonBase.uuid
1656 if(edge
.isBidirectional()){
1657 throw new RuntimeException("Bidirectional joining not supported!");
1660 fromField
= "relatedFrom.id";
1661 queryTermField
= "relatedFrom.titleCache";
1662 } else if(edge
.isInvers()) {
1663 fromField
= "relatedTo.id";
1664 queryTermField
= "relatedTo.titleCache";
1666 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1669 BooleanQuery finalQuery
= new BooleanQuery();
1671 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1672 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1674 BooleanQuery joinFromQuery
= new BooleanQuery();
1675 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1676 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1677 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1679 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1680 luceneSearch
.setSortFields(sortFields
);
1682 finalQuery
.add(joinQuery
, Occur
.MUST
);
1684 if(classification
!= null){
1685 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1687 luceneSearch
.setQuery(finalQuery
);
1689 if(highlightFragments
){
1690 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1692 return luceneSearch
;
1699 * @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)
1702 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1703 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1704 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1705 boolean highlightFragments
, Integer pageSize
,
1706 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1707 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1709 // FIXME: allow taxonomic ordering
1710 // 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";
1711 // this require building a special sort column by a special classBridge
1712 if(highlightFragments
){
1713 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1714 "currently not fully supported by this method and thus " +
1715 "may not work with common names and misapplied names.");
1718 // convert sets to lists
1719 List
<NamedArea
> namedAreaList
= null;
1720 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1721 if(namedAreas
!= null){
1722 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1723 namedAreaList
.addAll(namedAreas
);
1725 if(distributionStatus
!= null){
1726 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1727 distributionStatusList
.addAll(distributionStatus
);
1730 // set default if parameter is null
1731 if(searchModes
== null){
1732 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1735 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1737 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1738 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1741 ======== filtering by distribution , HOWTO ========
1743 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1744 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1745 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1746 which will be put into a FilteredQuersy in the end ?
1749 3. how does it work in spatial?
1751 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1752 - http://www.infoq.com/articles/LuceneSpatialSupport
1753 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1754 ------------------------------------------------------------------------
1757 A) use a separate distribution filter per index sub-query/search:
1758 - byTaxonSyonym (query TaxaonBase):
1759 use a join area filter (Distribution -> TaxonBase)
1760 - byCommonName (query DescriptionElementBase): use an area filter on
1761 DescriptionElementBase !!! PROBLEM !!!
1762 This cannot work since the distributions are different entities than the
1763 common names and thus these are different lucene documents.
1764 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1765 use a join area filter (Distribution -> TaxonBase)
1767 B) use a common distribution filter for all index sub-query/searches:
1768 - use a common join area filter (Distribution -> TaxonBase)
1769 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1770 PROBLEM in this case: we are losing the fragment highlighting for the
1771 common names, since the returned documents are always TaxonBases
1774 /* The QueryFactory for creating filter queries on Distributions should
1775 * The query factory used for the common names query cannot be reused
1776 * for this case, since we want to only record the text fields which are
1777 * actually used in the primary query
1779 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1781 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1784 // search for taxa or synonyms
1785 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1786 Class taxonBaseSubclass
= TaxonBase
.class;
1787 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1788 taxonBaseSubclass
= Taxon
.class;
1789 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1790 taxonBaseSubclass
= Synonym
.class;
1792 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
));
1793 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1794 /* A) does not work!!!!
1795 if(addDistributionFilter){
1796 // in this case we need a filter which uses a join query
1797 // to get the TaxonBase documents for the DescriptionElementBase documents
1798 // which are matching the areas in question
1799 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1801 distributionStatusList,
1802 distributionFilterQueryFactory
1804 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1807 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1808 // add additional area filter for synonyms
1809 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1810 String toField
= "accTaxon.id"; // id in TaxonBase index
1812 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1814 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1815 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1820 // search by CommonTaxonName
1821 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1823 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1824 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1825 "inDescription.taxon.id",
1827 QueryFactory
.addTypeRestriction(
1828 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1829 , CommonTaxonName
.class
1831 CommonTaxonName
.class);
1832 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1833 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1834 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1835 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1836 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1838 luceneSearches
.add(byCommonNameSearch
);
1840 /* A) does not work!!!!
1842 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1843 queryString, classification, null, languages, highlightFragments)
1845 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1846 if(addDistributionFilter){
1847 // in this case we are able to use DescriptionElementBase documents
1848 // which are matching the areas in question directly
1849 BooleanQuery byDistributionQuery = createByDistributionQuery(
1851 distributionStatusList,
1852 distributionFilterQueryFactory
1854 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1858 // search by misapplied names
1859 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1861 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1862 // which allows doing query time joins
1863 // finds the misapplied name (Taxon B) which is an misapplication for
1864 // a related Taxon A.
1866 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1867 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1868 queryString
, classification
, languages
, highlightFragments
));
1869 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1871 if(addDistributionFilter
){
1872 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1875 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1876 * Maybe this is a but in java itself java.
1878 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
1881 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
1883 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
1884 * will execute as expected:
1886 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1887 * String toField = "relation." + misappliedNameForUuid +".to.id";
1889 * Comparing both strings by the String.equals method returns true, so both String are identical.
1891 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
1892 * 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)
1893 * The bug is persistent after a reboot of the development computer.
1895 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1896 // String toField = "relation." + misappliedNameForUuid +".to.id";
1897 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
1898 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
1899 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
1901 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1902 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1903 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
1905 // debug code for bug described above
1906 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
1907 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
1909 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
1913 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
1914 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1917 if(addDistributionFilter
){
1920 // in this case we need a filter which uses a join query
1921 // to get the TaxonBase documents for the DescriptionElementBase documents
1922 // which are matching the areas in question
1924 // for toTaxa, doByCommonName
1925 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
1927 distributionStatusList
,
1928 distributionFilterQueryFactory
1930 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1933 if (addDistributionFilter
){
1934 multiSearch
.setFilter(multiIndexByAreaFilter
);
1936 // --- execute search
1937 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1939 // --- initialize taxa, highlight matches ....
1940 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1943 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1944 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1946 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1947 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1951 * @param namedAreaList at least one area must be in the list
1952 * @param distributionStatusList optional
1954 * @throws IOException
1956 protected Query
createByDistributionJoinQuery(
1957 List
<NamedArea
> namedAreaList
,
1958 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1959 QueryFactory queryFactory
1960 ) throws IOException
{
1962 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1963 String toField
= "id"; // id in TaxonBase index
1965 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
1967 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1969 return taxonAreaJoinQuery
;
1973 * @param namedAreaList
1974 * @param distributionStatusList
1975 * @param queryFactory
1978 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
1979 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
1980 BooleanQuery areaQuery
= new BooleanQuery();
1981 // area field from Distribution
1982 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
1984 // status field from Distribution
1985 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
1986 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
1989 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
1994 * This method has been primarily created for testing the area join query but might
1995 * also be useful in other situations
1997 * @param namedAreaList
1998 * @param distributionStatusList
1999 * @param classification
2000 * @param highlightFragments
2002 * @throws IOException
2004 protected LuceneSearch
prepareByDistributionSearch(
2005 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
2006 Classification classification
) throws IOException
{
2008 BooleanQuery finalQuery
= new BooleanQuery();
2010 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2012 // FIXME is this query factory using the wrong type?
2013 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2015 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2016 luceneSearch
.setSortFields(sortFields
);
2019 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2021 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2023 if(classification
!= null){
2024 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2027 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2028 luceneSearch
.setQuery(finalQuery
);
2030 return luceneSearch
;
2036 * @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)
2039 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2040 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2041 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2042 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2045 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2047 // --- execute search
2048 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2050 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2051 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2053 // --- initialize taxa, highlight matches ....
2054 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2055 @SuppressWarnings("rawtypes")
2056 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2057 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2059 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2060 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2066 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2067 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2068 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2070 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2071 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
);
2073 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2075 // --- execute search
2076 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2078 // --- initialize taxa, highlight matches ....
2079 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2081 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2082 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2083 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2085 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2086 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2088 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2089 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2096 * @param queryString
2097 * @param classification
2100 * @param highlightFragments
2101 * @param directorySelectClass
2104 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2105 String queryString
, Classification classification
, List
<Feature
> features
,
2106 List
<Language
> languages
, boolean highlightFragments
) {
2108 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2109 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2111 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2113 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2114 languages
, descriptionElementQueryFactory
);
2116 luceneSearch
.setSortFields(sortFields
);
2117 luceneSearch
.setCdmTypRestriction(clazz
);
2118 luceneSearch
.setQuery(finalQuery
);
2119 if(highlightFragments
){
2120 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2123 return luceneSearch
;
2127 * @param queryString
2128 * @param classification
2131 * @param descriptionElementQueryFactory
2134 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2135 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2136 BooleanQuery finalQuery
= new BooleanQuery();
2137 BooleanQuery textQuery
= new BooleanQuery();
2138 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2142 if(languages
== null || languages
.size() == 0){
2143 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2145 nameQuery
= new BooleanQuery();
2146 BooleanQuery languageSubQuery
= new BooleanQuery();
2147 for(Language lang
: languages
){
2148 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2150 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2151 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2153 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2156 // text field from TextData
2157 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2159 // --- TermBase fields - by representation ----
2160 // state field from CategoricalData
2161 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2163 // state field from CategoricalData
2164 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2166 // area field from Distribution
2167 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2169 // status field from Distribution
2170 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2172 finalQuery
.add(textQuery
, Occur
.MUST
);
2173 // --- classification ----
2175 if(classification
!= null){
2176 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2179 // --- IdentifieableEntity fields - by uuid
2180 if(features
!= null && features
.size() > 0 ){
2181 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2184 // the description must be associated with a taxon
2185 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2187 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2192 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2193 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2194 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2196 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2197 * or {@link MultilanguageTextFieldBridge }
2198 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2199 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2200 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2202 * TODO move to utiliy class !!!!!!!!
2204 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2206 if(stringBuilder
== null){
2207 stringBuilder
= new StringBuilder();
2209 if(languages
== null || languages
.size() == 0){
2210 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2212 for(Language lang
: languages
){
2213 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2216 return stringBuilder
;
2220 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2221 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2222 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2224 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2227 UUID nameUuid
= taxon
.getName().getUuid();
2228 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2229 String epithetOfTaxon
= null;
2230 String infragenericEpithetOfTaxon
= null;
2231 String infraspecificEpithetOfTaxon
= null;
2232 if (taxonName
.isSpecies()){
2233 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2234 } else if (taxonName
.isInfraGeneric()){
2235 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2236 } else if (taxonName
.isInfraSpecific()){
2237 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2239 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2240 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2241 List
<String
> taxonNames
= new ArrayList
<String
>();
2243 for (TaxonNode node
: nodes
){
2244 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2245 // List<String> synonymsEpithet = new ArrayList<String>();
2247 if (node
.getClassification().equals(classification
)){
2248 if (!node
.isTopmostNode()){
2249 TaxonNode parent
= node
.getParent();
2250 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2251 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2252 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2253 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2254 Rank rankOfTaxon
= taxonName
.getRank();
2257 //create inferred synonyms for species, subspecies
2258 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2260 Synonym inferredEpithet
= null;
2261 Synonym inferredGenus
= null;
2262 Synonym potentialCombination
= null;
2264 List
<String
> propertyPaths
= new ArrayList
<String
>();
2265 propertyPaths
.add("synonym");
2266 propertyPaths
.add("synonym.name");
2267 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2268 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2270 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2271 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2273 List
<TaxonRelationship
> taxonRelListParent
= null;
2274 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2275 if (doWithMisappliedNames
){
2276 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2277 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2281 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2284 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2285 Synonym syn
= synonymRelationOfParent
.getSynonym();
2287 inferredEpithet
= createInferredEpithets(taxon
,
2288 zooHashMap
, taxonName
, epithetOfTaxon
,
2289 infragenericEpithetOfTaxon
,
2290 infraspecificEpithetOfTaxon
,
2291 taxonNames
, parentName
,
2295 inferredSynonyms
.add(inferredEpithet
);
2296 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2297 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2300 if (doWithMisappliedNames
){
2302 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2303 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2305 inferredEpithet
= createInferredEpithets(taxon
,
2306 zooHashMap
, taxonName
, epithetOfTaxon
,
2307 infragenericEpithetOfTaxon
,
2308 infraspecificEpithetOfTaxon
,
2309 taxonNames
, parentName
,
2312 inferredSynonyms
.add(inferredEpithet
);
2313 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2314 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2318 if (!taxonNames
.isEmpty()){
2319 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2320 ZoologicalName name
;
2321 if (!synNotInCDM
.isEmpty()){
2322 inferredSynonymsToBeRemoved
.clear();
2324 for (Synonym syn
:inferredSynonyms
){
2325 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2326 if (!synNotInCDM
.contains(name
.getNameCache())){
2327 inferredSynonymsToBeRemoved
.add(syn
);
2331 // Remove identified Synonyms from inferredSynonyms
2332 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2333 inferredSynonyms
.remove(synonym
);
2338 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2341 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2342 TaxonNameBase synName
;
2343 ZoologicalName inferredSynName
;
2345 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2346 inferredGenus
= createInferredGenus(taxon
,
2347 zooHashMap
, taxonName
, epithetOfTaxon
,
2348 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2350 inferredSynonyms
.add(inferredGenus
);
2351 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2352 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2357 if (doWithMisappliedNames
){
2359 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2360 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2361 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2363 inferredSynonyms
.add(inferredGenus
);
2364 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2365 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2370 if (!taxonNames
.isEmpty()){
2371 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2372 ZoologicalName name
;
2373 if (!synNotInCDM
.isEmpty()){
2374 inferredSynonymsToBeRemoved
.clear();
2376 for (Synonym syn
:inferredSynonyms
){
2377 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2378 if (!synNotInCDM
.contains(name
.getNameCache())){
2379 inferredSynonymsToBeRemoved
.add(syn
);
2383 // Remove identified Synonyms from inferredSynonyms
2384 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2385 inferredSynonyms
.remove(synonym
);
2390 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2392 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2393 ZoologicalName inferredSynName
;
2394 //for all synonyms of the parent...
2395 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2396 TaxonNameBase synName
;
2397 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2398 synName
= synParent
.getName();
2400 HibernateProxyHelper
.deproxy(synParent
);
2402 // Set the sourceReference
2403 sourceReference
= synParent
.getSec();
2405 // Determine the idInSource
2406 String idInSourceParent
= getIdInSource(synParent
);
2408 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2409 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2410 String synParentInfragenericName
= null;
2411 String synParentSpecificEpithet
= null;
2413 if (parentSynZooName
.isInfraGeneric()){
2414 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2416 if (parentSynZooName
.isSpecies()){
2417 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2420 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2421 synonymsGenus.put(synGenusName, idInSource);
2424 //for all synonyms of the taxon
2426 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2428 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2429 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2430 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2432 synParentInfragenericName
,
2433 synParentSpecificEpithet
, syn
, zooHashMap
);
2435 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2436 inferredSynonyms
.add(potentialCombination
);
2437 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2438 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2445 if (doWithMisappliedNames
){
2447 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2449 TaxonNameBase misappliedParentName
;
2451 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2452 misappliedParentName
= misappliedParent
.getName();
2454 HibernateProxyHelper
.deproxy(misappliedParent
);
2456 // Set the sourceReference
2457 sourceReference
= misappliedParent
.getSec();
2459 // Determine the idInSource
2460 String idInSourceParent
= getIdInSource(misappliedParent
);
2462 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2463 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2464 String synParentInfragenericName
= null;
2465 String synParentSpecificEpithet
= null;
2467 if (parentSynZooName
.isInfraGeneric()){
2468 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2470 if (parentSynZooName
.isSpecies()){
2471 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2475 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2476 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2477 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2478 potentialCombination
= createPotentialCombination(
2479 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2481 synParentInfragenericName
,
2482 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2485 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2486 inferredSynonyms
.add(potentialCombination
);
2487 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2488 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2493 if (!taxonNames
.isEmpty()){
2494 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2495 ZoologicalName name
;
2496 if (!synNotInCDM
.isEmpty()){
2497 inferredSynonymsToBeRemoved
.clear();
2498 for (Synonym syn
:inferredSynonyms
){
2500 name
= (ZoologicalName
) syn
.getName();
2501 }catch (ClassCastException e
){
2502 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2504 if (!synNotInCDM
.contains(name
.getNameCache())){
2505 inferredSynonymsToBeRemoved
.add(syn
);
2508 // Remove identified Synonyms from inferredSynonyms
2509 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2510 inferredSynonyms
.remove(synonym
);
2516 logger
.info("The synonymrelationship type is not defined.");
2517 return inferredSynonyms
;
2524 return inferredSynonyms
;
2527 private Synonym
createPotentialCombination(String idInSourceParent
,
2528 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2529 String synParentInfragenericName
, String synParentSpecificEpithet
,
2530 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2531 Synonym potentialCombination
;
2532 Reference sourceReference
;
2533 ZoologicalName inferredSynName
;
2534 HibernateProxyHelper
.deproxy(syn
);
2536 // Set sourceReference
2537 sourceReference
= syn
.getSec();
2538 if (sourceReference
== null){
2539 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2541 if (!parentSynZooName
.getTaxa().isEmpty()){
2542 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2544 sourceReference
= taxon
.getSec();
2547 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2549 String synTaxonInfraSpecificName
= null;
2551 if (parentSynZooName
.isSpecies()){
2552 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2555 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2556 synonymsEpithet.add(epithetName);
2559 //create potential combinations...
2560 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2562 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2563 if (zooSynName
.isSpecies()){
2564 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2565 if (parentSynZooName
.isInfraGeneric()){
2566 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2569 if (zooSynName
.isInfraSpecific()){
2570 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2571 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2573 if (parentSynZooName
.isInfraGeneric()){
2574 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2578 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2580 // Set the sourceReference
2581 potentialCombination
.setSec(sourceReference
);
2584 // Determine the idInSource
2585 String idInSourceSyn
= getIdInSource(syn
);
2587 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2588 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2589 inferredSynName
.addSource(originalSource
);
2590 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2591 potentialCombination
.addSource(originalSource
);
2594 inferredSynName
.generateTitle();
2596 return potentialCombination
;
2599 private Synonym
createInferredGenus(Taxon taxon
,
2600 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2601 String epithetOfTaxon
, String genusOfTaxon
,
2602 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2605 Synonym inferredGenus
;
2606 TaxonNameBase synName
;
2607 ZoologicalName inferredSynName
;
2608 synName
=syn
.getName();
2609 HibernateProxyHelper
.deproxy(syn
);
2611 // Determine the idInSource
2612 String idInSourceSyn
= getIdInSource(syn
);
2613 String idInSourceTaxon
= getIdInSource(taxon
);
2614 // Determine the sourceReference
2615 Reference sourceReference
= syn
.getSec();
2617 //logger.warn(sourceReference.getTitleCache());
2619 synName
= syn
.getName();
2620 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2621 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2622 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2623 synonymsEpithet.add(synSpeciesEpithetName);
2626 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2627 //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...
2630 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2631 if (zooParentName
.isInfraGeneric()){
2632 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2635 if (taxonName
.isSpecies()){
2636 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2638 if (taxonName
.isInfraSpecific()){
2639 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2640 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2644 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2646 // Set the sourceReference
2647 inferredGenus
.setSec(sourceReference
);
2649 // Add the original source
2650 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2651 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2652 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2653 inferredGenus
.addSource(originalSource
);
2655 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2656 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2657 inferredSynName
.addSource(originalSource
);
2658 originalSource
= null;
2661 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2662 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2663 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2664 inferredGenus
.addSource(originalSource
);
2666 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2667 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2668 inferredSynName
.addSource(originalSource
);
2669 originalSource
= null;
2672 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2674 inferredSynName
.generateTitle();
2677 return inferredGenus
;
2680 private Synonym
createInferredEpithets(Taxon taxon
,
2681 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2682 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2683 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2684 TaxonNameBase parentName
, TaxonBase syn
) {
2686 Synonym inferredEpithet
;
2687 TaxonNameBase
<?
,?
> synName
;
2688 ZoologicalName inferredSynName
;
2689 HibernateProxyHelper
.deproxy(syn
);
2691 // Determine the idInSource
2692 String idInSourceSyn
= getIdInSource(syn
);
2693 String idInSourceTaxon
= getIdInSource(taxon
);
2694 // Determine the sourceReference
2695 Reference
<?
> sourceReference
= syn
.getSec();
2697 if (sourceReference
== null){
2698 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2699 sourceReference
= taxon
.getSec();
2702 synName
= syn
.getName();
2703 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2704 String synGenusName
= zooSynName
.getGenusOrUninomial();
2705 String synInfraGenericEpithet
= null;
2706 String synSpecificEpithet
= null;
2708 if (zooSynName
.getInfraGenericEpithet() != null){
2709 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2712 if (zooSynName
.isInfraSpecific()){
2713 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2716 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2717 synonymsGenus.put(synGenusName, idInSource);
2720 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2722 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2723 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2724 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2726 inferredSynName
.setGenusOrUninomial(synGenusName
);
2728 if (parentName
.isInfraGeneric()){
2729 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2731 if (taxonName
.isSpecies()){
2732 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2733 }else if (taxonName
.isInfraSpecific()){
2734 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2735 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2738 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2740 // Set the sourceReference
2741 inferredEpithet
.setSec(sourceReference
);
2743 /* Add the original source
2744 if (idInSource != null) {
2745 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2748 Reference citation = getCitation(syn);
2749 if (citation != null) {
2750 originalSource.setCitation(citation);
2751 inferredEpithet.addSource(originalSource);
2754 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2757 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2758 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2760 inferredEpithet
.addSource(originalSource
);
2762 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2763 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2765 inferredSynName
.addSource(originalSource
);
2769 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2771 inferredSynName
.generateTitle();
2772 return inferredEpithet
;
2776 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2777 * Very likely only useful for createInferredSynonyms().
2782 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2783 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2784 if (taxonName
== null) {
2785 taxonName
= zooHashMap
.get(uuid
);
2791 * Returns the idInSource for a given Synonym.
2794 private String
getIdInSource(TaxonBase taxonBase
) {
2795 String idInSource
= null;
2796 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2797 if (sources
.size() == 1) {
2798 IdentifiableSource source
= sources
.iterator().next();
2799 if (source
!= null) {
2800 idInSource
= source
.getIdInSource();
2802 } else if (sources
.size() > 1) {
2805 for (IdentifiableSource source
: sources
) {
2806 idInSource
+= source
.getIdInSource();
2807 if (count
< sources
.size()) {
2812 } else if (sources
.size() == 0){
2813 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2822 * Returns the citation for a given Synonym.
2825 private Reference
getCitation(Synonym syn
) {
2826 Reference citation
= null;
2827 Set
<IdentifiableSource
> sources
= syn
.getSources();
2828 if (sources
.size() == 1) {
2829 IdentifiableSource source
= sources
.iterator().next();
2830 if (source
!= null) {
2831 citation
= source
.getCitation();
2833 } else if (sources
.size() > 1) {
2834 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2841 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2842 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2844 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2845 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2846 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2848 return inferredSynonyms
;
2852 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2855 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2857 // TODO quickly implemented, create according dao !!!!
2858 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2859 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2860 List
<Classification
> list
= new ArrayList
<Classification
>();
2862 if (taxonBase
== null) {
2866 taxonBase
= load(taxonBase
.getUuid());
2868 if (taxonBase
instanceof Taxon
) {
2869 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2871 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2872 nodes
.addAll(taxon
.getTaxonNodes());
2875 for (TaxonNode node
: nodes
) {
2876 classifications
.add(node
.getClassification());
2878 list
.addAll(classifications
);