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
.IdentifiableEntity
;
66 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
67 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
68 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
69 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
70 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
71 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
72 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
73 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
74 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
75 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
76 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
77 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
78 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
79 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
80 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
81 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
82 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
83 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
84 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
85 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
86 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
87 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
88 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
89 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
90 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
91 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
92 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
93 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
94 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
95 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
96 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
97 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
98 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
99 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
100 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
101 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
102 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
103 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
104 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
105 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
106 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
108 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
109 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
110 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
111 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AbstractBeanInitializer
;
112 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
113 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
114 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
115 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
116 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
117 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
118 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
119 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
123 * @author a.kohlbecker
128 @Transactional(readOnly
= true)
129 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
130 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
132 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
134 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
136 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
140 private ITaxonNameDao nameDao
;
143 private INameService nameService
;
146 private ITaxonNodeService nodeService
;
150 private ICdmGenericDao genericDao
;
153 private IDescriptionService descriptionService
;
156 private IOrderedTermVocabularyDao orderedVocabularyDao
;
159 private IOccurrenceDao occurrenceDao
;
162 private AbstractBeanInitializer beanInitializer
;
165 private ILuceneIndexToolProvider luceneIndexToolProvider
;
170 public TaxonServiceImpl(){
171 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
175 * FIXME Candidate for harmonization
176 * rename searchByName ?
179 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
180 return dao
.getTaxaByName(name
, sec
);
184 * FIXME Candidate for harmonization
185 * list(Synonym.class, ...)
187 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
190 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
191 return dao
.getAllSynonyms(limit
, start
);
195 * FIXME Candidate for harmonization
196 * list(Taxon.class, ...)
198 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
201 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
202 return dao
.getAllTaxa(limit
, start
);
206 * FIXME Candidate for harmonization
207 * merge with getRootTaxa(Reference sec, ..., ...)
209 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
212 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
213 if (cdmFetch
== null){
214 cdmFetch
= CdmFetch
.NO_FETCH();
216 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
221 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
224 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
225 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
229 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
232 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
233 return dao
.getAllRelationships(limit
, start
);
237 * FIXME Candidate for harmonization
238 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
242 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
244 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
245 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
246 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
247 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
248 return taxonRelTypeVocabulary
;
255 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
258 @Transactional(readOnly
= false)
259 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
261 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
262 synonymName
.removeTaxonBase(synonym
);
263 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
264 taxonName
.removeTaxonBase(acceptedTaxon
);
266 synonym
.setName(taxonName
);
267 acceptedTaxon
.setName(synonymName
);
269 // the accepted taxon needs a new uuid because the concept has changed
270 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
271 //acceptedTaxon.setUuid(UUID.randomUUID());
276 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
280 @Transactional(readOnly
= false)
281 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
283 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
284 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
285 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
287 //check synonym is not homotypic
288 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
289 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
290 throw new HomotypicalGroupChangeException(message
);
293 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
295 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
296 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
298 for (Synonym heteroSynonym
: heteroSynonyms
){
299 if (synonym
.equals(heteroSynonym
)){
300 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
302 //move synonyms in same homotypic group to new accepted taxon
303 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
307 //synonym.getName().removeTaxonBase(synonym);
310 // deleteSynonym(synonym, taxon, false);
313 this.deleteSynonym(synonym
, acceptedTaxon
, new SynonymDeletionConfigurator());
315 } catch (Exception e
) {
316 logger
.info("Can't delete old synonym from database");
320 return newAcceptedTaxon
;
325 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
327 // Get name from synonym
328 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
330 // remove synonym from taxon
331 toTaxon
.removeSynonym(synonym
);
333 // Create a taxon with synonym name
334 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
336 // Add taxon relation
337 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
339 // since we are swapping names, we have to detach the name from the synonym completely.
340 // Otherwise the synonym will still be in the list of typified names.
341 synonym
.getName().removeTaxonBase(synonym
);
348 * @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)
350 @Transactional(readOnly
= false)
352 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
353 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
355 TaxonNameBase synonymName
= synonym
.getName();
356 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
360 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
361 newHomotypicalGroup
.addTypifiedName(synonymName
);
363 //remove existing basionym relationships
364 synonymName
.removeBasionyms();
366 //add basionym relationship
367 if (setBasionymRelationIfApplicable
){
368 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
369 for (TaxonNameBase basionym
: basionyms
){
370 synonymName
.addBasionym(basionym
);
374 //set synonym relationship correctly
375 // SynonymRelationship relToTaxon = null;
376 boolean relToTargetTaxonExists
= false;
377 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
378 for (SynonymRelationship rel
: existingRelations
){
379 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
380 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
381 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
382 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
383 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
384 rel
.setType(newRelationType
);
385 //TODO handle citation and microCitation
388 relToTargetTaxonExists
= true;
390 if (removeFromOtherTaxa
){
391 acceptedTaxon
.removeSynonym(synonym
, false);
397 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
398 Taxon acceptedTaxon
= targetTaxon
;
399 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
400 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
401 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
402 //TODO handle citation and microCitation
403 Reference citation
= null;
404 String microCitation
= null;
405 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
412 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
415 @Transactional(readOnly
= false)
416 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
418 clazz
= TaxonBase
.class;
420 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
425 protected void setDao(ITaxonDao dao
) {
430 * @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)
433 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
434 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
436 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
437 if(numberOfResults
> 0) { // no point checking again
438 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
441 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
445 * @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)
448 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
449 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
451 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
452 if(numberOfResults
> 0) { // no point checking again
453 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
460 * @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)
463 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
464 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
466 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
467 if(numberOfResults
> 0) { // no point checking again
468 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
474 * @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)
477 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
478 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
480 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
481 if(numberOfResults
> 0) { // no point checking again
482 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
484 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
488 * @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)
491 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
492 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
494 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
495 if(numberOfResults
> 0) { // no point checking again
496 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
502 * @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)
505 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
506 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
508 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
509 if(numberOfResults
> 0) { // no point checking again
510 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
512 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
517 * @param includeRelationships
521 * @param propertyPaths
522 * @return an List which is not specifically ordered
525 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
526 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
528 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
529 relatedTaxa
.remove(taxon
);
530 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
536 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
537 * <code>taxon</code> supplied as parameter.
540 * @param includeRelationships
542 * @param maxDepth can be <code>null</code> for infinite depth
545 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
551 if(maxDepth
!= null) {
554 if(logger
.isDebugEnabled()){
555 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
557 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
558 for (TaxonRelationship taxRel
: taxonRelationships
) {
561 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
564 // filter by includeRelationships
565 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
566 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
567 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
568 if(logger
.isDebugEnabled()){
569 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
571 taxa
.add(taxRel
.getToTaxon());
572 if(maxDepth
== null || maxDepth
> 0) {
573 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
576 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
577 taxa
.add(taxRel
.getFromTaxon());
578 if(logger
.isDebugEnabled()){
579 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
581 if(maxDepth
== null || maxDepth
> 0) {
582 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
592 * @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)
595 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
596 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
598 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
599 if(numberOfResults
> 0) { // no point checking again
600 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
603 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
607 * @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)
610 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
611 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
613 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
614 if(numberOfResults
> 0) { // no point checking again
615 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
618 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
622 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
625 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
626 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
627 return t
.getHomotypicSynonymsByHomotypicGroup();
631 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
634 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
635 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
636 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
637 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
638 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
639 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
641 return heterotypicSynonymyGroups
;
645 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
647 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
648 // Class<? extends TaxonBase> clazz = null;
649 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
650 // clazz = TaxonBase.class;
651 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
652 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
653 // } else if(configurator.isDoTaxa()) {
654 // clazz = Taxon.class;
655 // //propertyPath = configurator.getTaxonPropertyPath();
656 // } else if (configurator.isDoSynonyms()) {
657 // clazz = Synonym.class;
658 // //propertyPath = configurator.getSynonymPropertyPath();
662 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
667 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
670 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
672 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
673 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
674 List
<TaxonBase
> taxa
= null;
677 long numberTaxaResults
= 0L;
680 List
<String
> propertyPath
= new ArrayList
<String
>();
681 if(configurator
.getTaxonPropertyPath() != null){
682 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
686 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
687 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
689 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
690 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
691 configurator
.getNamedAreas());
694 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
695 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
696 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
697 configurator
.getMatchMode(), configurator
.getNamedAreas(),
698 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
702 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
705 results
.addAll(taxa
);
708 numberOfResults
+= numberTaxaResults
;
710 // Names without taxa
711 if (configurator
.isDoNamesWithoutTaxa()) {
712 int numberNameResults
= 0;
714 List
<?
extends TaxonNameBase
<?
,?
>> names
=
715 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
716 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
717 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
718 if (names
.size() > 0) {
719 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
720 if (taxonName
.getTaxonBases().size() == 0) {
721 results
.add(taxonName
);
725 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
726 numberOfResults
+= numberNameResults
;
730 // Taxa from common names
732 if (configurator
.isDoTaxaByCommonNames()) {
733 taxa
= new ArrayList
<TaxonBase
>();
734 numberTaxaResults
= 0;
735 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
736 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
738 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
739 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
740 for( Object
[] entry
: commonNameResults
) {
741 taxa
.add((TaxonBase
) entry
[0]);
745 results
.addAll(taxa
);
747 numberOfResults
+= numberTaxaResults
;
751 return new DefaultPagerImpl
<IdentifiableEntity
>
752 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
755 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
756 return dao
.getUuidAndTitleCache();
760 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
763 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
764 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
765 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
766 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
767 for (TaxonDescription taxDesc
: descriptions
){
768 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
769 for (DescriptionElementBase descElem
: elements
){
770 for(Media media
: descElem
.getMedia()){
772 //find the best matching representation
773 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
782 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
785 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
786 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
791 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
794 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
795 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
796 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
798 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
799 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
801 if (limitToGalleries
== null) {
802 limitToGalleries
= false;
805 // --- resolve related taxa
806 if (includeRelationships
!= null) {
807 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
810 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
812 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
813 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
814 // --- TaxonDescriptions
815 for (Taxon t
: taxa
) {
816 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
818 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
819 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
820 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
821 for (Media media
: element
.getMedia()) {
822 taxonMedia
.add(media
);
829 if(includeOccurrences
!= null && includeOccurrences
) {
830 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
832 for (Taxon t
: taxa
) {
833 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
835 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
837 // direct media removed from specimen #3597
838 // taxonMedia.addAll(occurrence.getMedia());
840 // SpecimenDescriptions
841 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
842 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
843 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
844 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
845 for (DescriptionElementBase element
: elements
) {
846 for (Media media
: element
.getMedia()) {
847 taxonMedia
.add(media
);
854 //TODO why may collections have media attached? #
855 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
856 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
857 if (derivedUnit
.getCollection() != null){
858 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
862 // pherograms & gelPhotos
863 if (occurrence
.isInstanceOf(DnaSample
.class)) {
864 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
865 Set
<Sequence
> sequences
= dnaSample
.getSequences();
866 //we do show only those gelPhotos which lead to a consensus sequence
867 for (Sequence sequence
: sequences
) {
868 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
869 for (SingleRead singleRead
: sequence
.getSingleReads()){
870 Amplification amplification
= singleRead
.getAmplification();
871 dnaRelatedMedia
.add(amplification
.getGelPhoto());
872 dnaRelatedMedia
.add(singleRead
.getPherogram());
873 dnaRelatedMedia
.remove(null);
875 taxonMedia
.addAll(dnaRelatedMedia
);
882 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
883 // --- TaxonNameDescription
884 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
885 for (Taxon t
: taxa
) {
886 nameDescriptions
.addAll(t
.getName().getDescriptions());
888 for(TaxonNameDescription nameDescription
: nameDescriptions
){
889 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
890 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
891 for (DescriptionElementBase element
: elements
) {
892 for (Media media
: element
.getMedia()) {
893 taxonMedia
.add(media
);
900 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
905 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
908 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
909 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
913 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
916 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
917 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
921 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
924 public int countAllRelationships() {
925 return this.dao
.countAllRelationships();
932 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
935 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
936 return this.dao
.findIdenticalTaxonNames(propertyPath
);
941 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
944 public UUID
deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
, Classification classification
) throws DataChangeNoRollbackException
{
946 config
= new TaxonDeletionConfigurator();
949 // SynonymRelationShip
950 if (config
.isDeleteSynonymRelations()){
951 boolean removeSynonymNameFromHomotypicalGroup
= false;
952 for (SynonymRelationship synRel
: taxon
.getSynonymRelations()){
953 Synonym synonym
= synRel
.getSynonym();
954 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
955 if (config
.isDeleteSynonymsIfPossible()){
957 boolean newHomotypicGroupIfNeeded
= true;
958 SynonymDeletionConfigurator synConfig
= new SynonymDeletionConfigurator();
960 deleteSynonym(synonym
, taxon
, synConfig
);
962 deleteSynonymRelationships(synonym
, taxon
);
968 if (! config
.isDeleteTaxonRelationships()){
969 if (taxon
.getTaxonRelations().size() > 0){
970 String message
= "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";
971 throw new ReferencedObjectUndeletableException(message
);
974 for (TaxonRelationship taxRel
: taxon
.getTaxonRelations()){
978 if (config
.isDeleteMisappliedNamesAndInvalidDesignations()){
979 if (taxRel
.getType().equals(TaxonRelationshipType
.MISAPPLIED_NAME_FOR()) || taxRel
.getType().equals(TaxonRelationshipType
.INVALID_DESIGNATION_FOR())){
980 if (taxon
.equals(taxRel
.getToTaxon())){
981 this.deleteTaxon(taxRel
.getFromTaxon(), config
, classification
);
985 taxon
.removeTaxonRelation(taxRel
);
986 /*if (taxFrom.equals(taxon)){
988 this.deleteTaxon(taxTo, taxConf, classification);
989 } catch(DataChangeNoRollbackException e){
990 logger.debug("A related taxon will not be deleted." + e.getMessage());
994 this.deleteTaxon(taxFrom, taxConf, classification);
995 } catch(DataChangeNoRollbackException e){
996 logger.debug("A related taxon will not be deleted." + e.getMessage());
1007 if (config
.isDeleteDescriptions()){
1008 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
1010 for (TaxonDescription desc
: descriptions
){
1011 //TODO use description delete configurator ?
1012 //FIXME check if description is ALWAYS deletable
1013 if (desc
.getDescribedSpecimenOrObservation() != null){
1014 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
1015 " which also describes specimens or abservations";
1016 throw new ReferencedObjectUndeletableException(message
);
1018 descriptionService
.delete(desc
);
1019 taxon
.removeDescription(desc
);
1024 //check references with only reverse mapping
1025 String message
= checkForReferences(taxon
);
1026 if (message
!= null){
1027 throw new ReferencedObjectUndeletableException(message
.toString());
1030 if (! config
.isDeleteTaxonNodes() || (!config
.isDeleteInAllClassifications() && classification
== null )){
1031 if (taxon
.getTaxonNodes().size() > 0){
1032 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.";
1033 throw new ReferencedObjectUndeletableException(message
);
1036 if (taxon
.getTaxonNodes().size() != 0){
1037 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1038 Iterator
<TaxonNode
> iterator
= nodes
.iterator();
1039 TaxonNode node
= null;
1040 boolean deleteChildren
;
1041 if (config
.getTaxonNodeConfig().getChildHandling().equals(ChildHandling
.DELETE
)){
1042 deleteChildren
= true;
1044 deleteChildren
= false;
1046 boolean success
= true;
1047 if (!config
.isDeleteInAllClassifications() && !(classification
== null)){
1048 while (iterator
.hasNext()){
1049 node
= iterator
.next();
1050 if (node
.getClassification().equals(classification
)){
1056 success
=taxon
.removeTaxonNode(node
, deleteChildren
);
1058 message
= "Taxon is not used in defined classification";
1059 throw new DataChangeNoRollbackException(message
);
1061 } else if (config
.isDeleteInAllClassifications()){
1062 List
<TaxonNode
> nodesList
= new ArrayList
<TaxonNode
>();
1063 nodesList
.addAll(taxon
.getTaxonNodes());
1065 for (TaxonNode taxonNode
: nodesList
){
1067 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1068 for (Object childNode
: childNodes
){
1069 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1070 deleteTaxon(childNodeCast
.getTaxon(), config
, classification
);
1074 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1075 deleteTaxon(childNode.getTaxon(), config, classification);
1078 //taxon.removeTaxonNode(taxonNode);
1080 Object
[] childNodes
= taxonNode
.getChildNodes().toArray();
1081 for (Object childNode
: childNodes
){
1082 TaxonNode childNodeCast
= (TaxonNode
) childNode
;
1083 taxonNode
.getParent().addChildNode(childNodeCast
, childNodeCast
.getReference(), childNodeCast
.getMicroReference());
1086 //taxon.removeTaxonNode(taxonNode);
1092 nodeService
.deleteTaxonNodes(nodesList
);
1096 message
= "The taxon node could not be deleted.";
1097 throw new DataChangeNoRollbackException(message
);
1102 if (config
.isDeleteNameIfPossible()){
1105 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1106 TaxonNameBase name
= (TaxonNameBase
)HibernateProxyHelper
.deproxy(taxon
.getName());
1107 //check whether taxon will be deleted or not
1108 if (taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0){
1109 taxon
= (Taxon
) HibernateProxyHelper
.deproxy(taxon
);
1110 name
.removeTaxonBase(taxon
);
1111 nameService
.save(name
);
1112 nameService
.delete(name
, config
.getNameDeletionConfig());
1114 } catch (ReferencedObjectUndeletableException e
) {
1116 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1122 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1124 for (TaxonDescription desc: descriptions){
1125 if (config.isDeleteDescriptions()){
1126 //TODO use description delete configurator ?
1127 //FIXME check if description is ALWAYS deletable
1128 taxon.removeDescription(desc);
1129 descriptionService.delete(desc);
1131 if (desc.getDescribedSpecimenOrObservations().size()>0){
1132 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1133 " which also describes specimens or observations";
1134 throw new ReferencedObjectUndeletableException(message);
1141 if (taxon
.getTaxonNodes() == null || taxon
.getTaxonNodes().size()== 0){
1143 return taxon
.getUuid();
1145 message
= "Taxon can't be deleted as it is used in another Taxonnode";
1146 throw new ReferencedObjectUndeletableException(message
);
1152 private String
checkForReferences(Taxon taxon
){
1153 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1154 for (CdmBase referencingObject
: referencingObjects
){
1155 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1156 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1157 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";
1164 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
1165 String message
= "Taxon" + taxon
.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1170 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1171 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1175 referencingObjects
= null;
1179 @Transactional(readOnly
= false)
1180 public UUID
delete(Synonym syn
){
1181 UUID result
= syn
.getUuid();
1182 this.deleteSynonym(syn
, null);
1187 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1189 @Transactional(readOnly
= false)
1191 public void deleteSynonym(Synonym synonym
, SynonymDeletionConfigurator config
) {
1192 deleteSynonym(synonym
, null, config
);
1198 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1200 @Transactional(readOnly
= false)
1202 public void deleteSynonym(Synonym synonym
, Taxon taxon
, SynonymDeletionConfigurator config
) {
1203 if (synonym
== null){
1206 if (config
== null){
1207 config
= new SynonymDeletionConfigurator();
1209 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1211 //remove synonymRelationship
1212 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1214 taxonSet
.add(taxon
);
1216 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1218 for (Taxon relatedTaxon
: taxonSet
){
1219 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1220 relatedTaxon
.removeSynonym(synonym
, config
.isNewHomotypicGroupIfNeeded());
1222 this.saveOrUpdate(synonym
);
1224 //TODO remove name from homotypical group?
1226 //remove synonym (if necessary)
1229 if (synonym
.getSynonymRelations().isEmpty()){
1230 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1231 synonym
.setName(null);
1232 dao
.delete(synonym
);
1234 //remove name if possible (and required)
1235 if (name
!= null && config
.isDeleteNameIfPossible()){
1237 nameService
.delete(name
, config
.getNameDeletionConfig());
1238 }catch (ReferencedObjectUndeletableException ex
){
1239 System
.err
.println("Name wasn't deleted as it is referenced");
1240 if (logger
.isDebugEnabled()) {
1241 logger
.debug("Name wasn't deleted as it is referenced");
1250 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1253 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1255 return this.dao
.findIdenticalNamesNew(propertyPath
);
1259 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1262 public String
getPhylumName(TaxonNameBase name
){
1263 return this.dao
.getPhylumName(name
);
1267 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1270 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1271 return dao
.deleteSynonymRelationships(syn
, taxon
);
1275 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1278 public long deleteSynonymRelationships(Synonym syn
) {
1279 return dao
.deleteSynonymRelationships(syn
, null);
1284 * @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)
1287 public List
<SynonymRelationship
> listSynonymRelationships(
1288 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1289 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1290 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1292 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1293 if(numberOfResults
> 0) { // no point checking again
1294 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1300 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1303 public Taxon
findBestMatchingTaxon(String taxonName
) {
1304 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1305 config
.setTaxonNameTitle(taxonName
);
1306 return findBestMatchingTaxon(config
);
1312 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1314 Taxon bestCandidate
= null;
1316 // 1. search for acceptet taxa
1317 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1318 boolean bestCandidateMatchesSecUuid
= false;
1319 boolean bestCandidateIsInClassification
= false;
1320 int countEqualCandidates
= 0;
1321 for(TaxonBase taxonBaseCandidate
: taxonList
){
1322 if(taxonBaseCandidate
instanceof Taxon
){
1323 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1324 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1325 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1327 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1328 bestCandidate
= newCanditate
;
1329 countEqualCandidates
= 1;
1330 bestCandidateMatchesSecUuid
= true;
1334 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1335 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1337 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1338 bestCandidate
= newCanditate
;
1339 countEqualCandidates
= 1;
1340 bestCandidateIsInClassification
= true;
1343 if (bestCandidate
== null){
1344 bestCandidate
= newCanditate
;
1345 countEqualCandidates
= 1;
1349 }else{ //not Taxon.class
1352 countEqualCandidates
++;
1355 if (bestCandidate
!= null){
1356 if(countEqualCandidates
> 1){
1357 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1358 return bestCandidate
;
1360 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1361 return bestCandidate
;
1366 // 2. search for synonyms
1367 if (config
.isIncludeSynonyms()){
1368 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1369 for(TaxonBase taxonBase
: synonymList
){
1370 if(taxonBase
instanceof Synonym
){
1371 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1372 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1373 if(!acceptetdCandidates
.isEmpty()){
1374 bestCandidate
= acceptetdCandidates
.iterator().next();
1375 if(acceptetdCandidates
.size() == 1){
1376 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1377 return bestCandidate
;
1379 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1380 return bestCandidate
;
1382 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1388 } catch (Exception e
){
1390 e
.printStackTrace();
1393 return bestCandidate
;
1396 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1397 UUID configClassificationUuid
= config
.getClassificationUuid();
1398 if (configClassificationUuid
== null){
1401 for (TaxonNode node
: taxon
.getTaxonNodes()){
1402 UUID classUuid
= node
.getClassification().getUuid();
1403 if (configClassificationUuid
.equals(classUuid
)){
1410 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1411 UUID configSecUuid
= config
.getSecUuid();
1412 if (configSecUuid
== null){
1415 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1416 return configSecUuid
.equals(taxonSecUuid
);
1420 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1423 public Synonym
findBestMatchingSynonym(String taxonName
) {
1424 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1425 if(! synonymList
.isEmpty()){
1426 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1427 if(synonymList
.size() == 1){
1428 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1431 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1440 * @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)
1443 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1444 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1446 Synonym synonym
= oldSynonymRelation
.getSynonym();
1447 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1448 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1449 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1450 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1451 //set default relationship type
1452 if (newSynonymRelationshipType
== null){
1453 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1455 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1457 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1458 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1459 boolean isSingleInGroup
= !(hgSize
> 1);
1461 if (! isSingleInGroup
){
1462 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1463 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1464 if (isHomotypicToAccepted
){
1465 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.";
1466 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1467 message
= String
.format(message
, homotypicRelatives
);
1468 throw new HomotypicalGroupChangeException(message
);
1470 if (! moveHomotypicGroup
){
1471 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.";
1472 throw new HomotypicalGroupChangeException(message
);
1475 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1477 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1479 SynonymRelationship result
= null;
1480 //move all synonyms to new taxon
1481 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1482 for (Synonym syn
: homotypicSynonyms
){
1483 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1484 for (SynonymRelationship synRelation
: synRelations
){
1485 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1486 Reference
<?
> newReference
= reference
;
1487 if (newReference
== null && keepReference
){
1488 newReference
= synRelation
.getCitation();
1490 String newRefDetail
= referenceDetail
;
1491 if (newRefDetail
== null && keepReference
){
1492 newRefDetail
= synRelation
.getCitationMicroReference();
1494 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1495 fromTaxon
.removeSynonymRelation(synRelation
, false);
1497 //change homotypic group of synonym if relType is 'homotypic'
1498 // if (newRelTypeIsHomotypic){
1499 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1502 if (synRelation
.equals(oldSynonymRelation
)){
1503 result
= newSynRelation
;
1509 saveOrUpdate(newTaxon
);
1510 //Assert that there is a result
1511 if (result
== null){
1512 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1513 throw new IllegalStateException(message
);
1519 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1522 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1523 return dao
.getUuidAndTitleCacheTaxon();
1527 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1530 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1531 return dao
.getUuidAndTitleCacheSynonym();
1535 * @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)
1538 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1539 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1540 Classification classification
, List
<Language
> languages
,
1541 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1544 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
);
1546 // --- execute search
1547 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1549 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1550 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1552 // --- initialize taxa, thighlight matches ....
1553 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1554 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1555 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1557 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1558 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1562 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1563 Classification classification
,
1564 Integer pageSize
, Integer pageNumber
,
1565 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1567 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1569 // --- execute search
1570 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1572 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1573 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1575 // --- initialize taxa, thighlight matches ....
1576 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1577 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1578 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1580 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1581 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1586 * @param queryString
1587 * @param classification
1589 * @param highlightFragments
1590 * @param directorySelectClass
1593 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1594 boolean highlightFragments
) {
1595 BooleanQuery finalQuery
= new BooleanQuery();
1596 BooleanQuery textQuery
= new BooleanQuery();
1598 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1599 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1601 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1602 luceneSearch
.setSortFields(sortFields
);
1604 // ---- search criteria
1605 luceneSearch
.setCdmTypRestriction(clazz
);
1607 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1608 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1610 finalQuery
.add(textQuery
, Occur
.MUST
);
1612 if(classification
!= null){
1613 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1615 luceneSearch
.setQuery(finalQuery
);
1617 if(highlightFragments
){
1618 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1620 return luceneSearch
;
1624 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1625 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1626 * drawback of requiring to do the join an indexing time.
1627 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1629 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1631 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1632 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1635 * @param queryString
1636 * @param classification
1638 * @param highlightFragments
1640 * @throws IOException
1642 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1643 boolean highlightFragments
) throws IOException
{
1646 String queryTermField
;
1647 String toField
= "id"; // TaxonBase.uuid
1649 if(edge
.isBidirectional()){
1650 throw new RuntimeException("Bidirectional joining not supported!");
1653 fromField
= "relatedFrom.id";
1654 queryTermField
= "relatedFrom.titleCache";
1655 } else if(edge
.isInvers()) {
1656 fromField
= "relatedTo.id";
1657 queryTermField
= "relatedTo.titleCache";
1659 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1662 BooleanQuery finalQuery
= new BooleanQuery();
1664 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1665 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1667 BooleanQuery joinFromQuery
= new BooleanQuery();
1668 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1669 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1670 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1672 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1673 luceneSearch
.setSortFields(sortFields
);
1675 finalQuery
.add(joinQuery
, Occur
.MUST
);
1677 if(classification
!= null){
1678 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1680 luceneSearch
.setQuery(finalQuery
);
1682 if(highlightFragments
){
1683 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1685 return luceneSearch
;
1692 * @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)
1695 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1696 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1697 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1698 boolean highlightFragments
, Integer pageSize
,
1699 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1700 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1702 // FIXME: allow taxonomic ordering
1703 // 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";
1704 // this require building a special sort column by a special classBridge
1705 if(highlightFragments
){
1706 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1707 "currently not fully supported by this method and thus " +
1708 "may not work with common names and misapplied names.");
1711 // convert sets to lists
1712 List
<NamedArea
> namedAreaList
= null;
1713 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1714 if(namedAreas
!= null){
1715 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1716 namedAreaList
.addAll(namedAreas
);
1718 if(distributionStatus
!= null){
1719 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1720 distributionStatusList
.addAll(distributionStatus
);
1723 // set default if parameter is null
1724 if(searchModes
== null){
1725 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1728 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1730 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1731 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1734 ======== filtering by distribution , HOWTO ========
1736 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1737 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1738 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1739 which will be put into a FilteredQuersy in the end ?
1742 3. how does it work in spatial?
1744 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1745 - http://www.infoq.com/articles/LuceneSpatialSupport
1746 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1747 ------------------------------------------------------------------------
1750 A) use a separate distribution filter per index sub-query/search:
1751 - byTaxonSyonym (query TaxaonBase):
1752 use a join area filter (Distribution -> TaxonBase)
1753 - byCommonName (query DescriptionElementBase): use an area filter on
1754 DescriptionElementBase !!! PROBLEM !!!
1755 This cannot work since the distributions are different entities than the
1756 common names and thus these are different lucene documents.
1757 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1758 use a join area filter (Distribution -> TaxonBase)
1760 B) use a common distribution filter for all index sub-query/searches:
1761 - use a common join area filter (Distribution -> TaxonBase)
1762 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1763 PROBLEM in this case: we are losing the fragment highlighting for the
1764 common names, since the returned documents are always TaxonBases
1767 /* The QueryFactory for creating filter queries on Distributions should
1768 * The query factory used for the common names query cannot be reused
1769 * for this case, since we want to only record the text fields which are
1770 * actually used in the primary query
1772 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1774 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1777 // search for taxa or synonyms
1778 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1779 Class taxonBaseSubclass
= TaxonBase
.class;
1780 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1781 taxonBaseSubclass
= Taxon
.class;
1782 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1783 taxonBaseSubclass
= Synonym
.class;
1785 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
));
1786 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1787 /* A) does not work!!!!
1788 if(addDistributionFilter){
1789 // in this case we need a filter which uses a join query
1790 // to get the TaxonBase documents for the DescriptionElementBase documents
1791 // which are matching the areas in question
1792 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1794 distributionStatusList,
1795 distributionFilterQueryFactory
1797 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1800 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1801 // add additional area filter for synonyms
1802 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1803 String toField
= "accTaxon.id"; // id in TaxonBase index
1805 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1807 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1808 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1813 // search by CommonTaxonName
1814 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1816 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1817 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1818 "inDescription.taxon.id",
1820 QueryFactory
.addTypeRestriction(
1821 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1822 , CommonTaxonName
.class
1824 CommonTaxonName
.class);
1825 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1826 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1827 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1828 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1829 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1831 luceneSearches
.add(byCommonNameSearch
);
1833 /* A) does not work!!!!
1835 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1836 queryString, classification, null, languages, highlightFragments)
1838 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1839 if(addDistributionFilter){
1840 // in this case we are able to use DescriptionElementBase documents
1841 // which are matching the areas in question directly
1842 BooleanQuery byDistributionQuery = createByDistributionQuery(
1844 distributionStatusList,
1845 distributionFilterQueryFactory
1847 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1851 // search by misapplied names
1852 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1854 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1855 // which allows doing query time joins
1856 // finds the misapplied name (Taxon B) which is an misapplication for
1857 // a related Taxon A.
1859 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1860 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1861 queryString
, classification
, languages
, highlightFragments
));
1862 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1864 if(addDistributionFilter
){
1865 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1868 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1869 * Maybe this is a but in java itself java.
1871 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
1874 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
1876 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
1877 * will execute as expected:
1879 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1880 * String toField = "relation." + misappliedNameForUuid +".to.id";
1882 * Comparing both strings by the String.equals method returns true, so both String are identical.
1884 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
1885 * 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)
1886 * The bug is persistent after a reboot of the development computer.
1888 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1889 // String toField = "relation." + misappliedNameForUuid +".to.id";
1890 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
1891 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
1892 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
1894 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1895 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1896 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
1898 // debug code for bug described above
1899 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
1900 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
1902 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
1906 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
1907 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1910 if(addDistributionFilter
){
1913 // in this case we need a filter which uses a join query
1914 // to get the TaxonBase documents for the DescriptionElementBase documents
1915 // which are matching the areas in question
1917 // for toTaxa, doByCommonName
1918 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
1920 distributionStatusList
,
1921 distributionFilterQueryFactory
1923 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1926 if (addDistributionFilter
){
1927 multiSearch
.setFilter(multiIndexByAreaFilter
);
1929 // --- execute search
1930 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1932 // --- initialize taxa, highlight matches ....
1933 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1936 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1937 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1939 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1940 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1944 * @param namedAreaList at least one area must be in the list
1945 * @param distributionStatusList optional
1947 * @throws IOException
1949 protected Query
createByDistributionJoinQuery(
1950 List
<NamedArea
> namedAreaList
,
1951 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1952 QueryFactory queryFactory
1953 ) throws IOException
{
1955 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1956 String toField
= "id"; // id in TaxonBase index
1958 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
1960 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1962 return taxonAreaJoinQuery
;
1966 * @param namedAreaList
1967 * @param distributionStatusList
1968 * @param queryFactory
1971 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
1972 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
1973 BooleanQuery areaQuery
= new BooleanQuery();
1974 // area field from Distribution
1975 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
1977 // status field from Distribution
1978 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
1979 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
1982 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
1987 * This method has been primarily created for testing the area join query but might
1988 * also be useful in other situations
1990 * @param namedAreaList
1991 * @param distributionStatusList
1992 * @param classification
1993 * @param highlightFragments
1995 * @throws IOException
1997 protected LuceneSearch
prepareByDistributionSearch(
1998 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1999 Classification classification
) throws IOException
{
2001 BooleanQuery finalQuery
= new BooleanQuery();
2003 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
2005 // FIXME is this query factory using the wrong type?
2006 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
2008 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
2009 luceneSearch
.setSortFields(sortFields
);
2012 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
2014 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
2016 if(classification
!= null){
2017 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
2020 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
2021 luceneSearch
.setQuery(finalQuery
);
2023 return luceneSearch
;
2029 * @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)
2032 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
2033 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
2034 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
2035 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
2038 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
2040 // --- execute search
2041 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
2043 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2044 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2046 // --- initialize taxa, highlight matches ....
2047 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
2048 @SuppressWarnings("rawtypes")
2049 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2050 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2052 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2053 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2059 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
2060 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
2061 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
2063 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
2064 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
);
2066 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
2068 // --- execute search
2069 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
2071 // --- initialize taxa, highlight matches ....
2072 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
2074 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
2075 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
2076 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
2078 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
2079 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
2081 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
2082 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
2089 * @param queryString
2090 * @param classification
2093 * @param highlightFragments
2094 * @param directorySelectClass
2097 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
2098 String queryString
, Classification classification
, List
<Feature
> features
,
2099 List
<Language
> languages
, boolean highlightFragments
) {
2101 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
2102 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
2104 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
2106 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
2107 languages
, descriptionElementQueryFactory
);
2109 luceneSearch
.setSortFields(sortFields
);
2110 luceneSearch
.setCdmTypRestriction(clazz
);
2111 luceneSearch
.setQuery(finalQuery
);
2112 if(highlightFragments
){
2113 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
2116 return luceneSearch
;
2120 * @param queryString
2121 * @param classification
2124 * @param descriptionElementQueryFactory
2127 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
2128 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
2129 BooleanQuery finalQuery
= new BooleanQuery();
2130 BooleanQuery textQuery
= new BooleanQuery();
2131 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
2135 if(languages
== null || languages
.size() == 0){
2136 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
2138 nameQuery
= new BooleanQuery();
2139 BooleanQuery languageSubQuery
= new BooleanQuery();
2140 for(Language lang
: languages
){
2141 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
2143 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
2144 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
2146 textQuery
.add(nameQuery
, Occur
.SHOULD
);
2149 // text field from TextData
2150 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
2152 // --- TermBase fields - by representation ----
2153 // state field from CategoricalData
2154 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
2156 // state field from CategoricalData
2157 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
2159 // area field from Distribution
2160 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
2162 // status field from Distribution
2163 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
2165 finalQuery
.add(textQuery
, Occur
.MUST
);
2166 // --- classification ----
2168 if(classification
!= null){
2169 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2172 // --- IdentifieableEntity fields - by uuid
2173 if(features
!= null && features
.size() > 0 ){
2174 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2177 // the description must be associated with a taxon
2178 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2180 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2185 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2186 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2187 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2189 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2190 * or {@link MultilanguageTextFieldBridge }
2191 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2192 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2193 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2195 * TODO move to utiliy class !!!!!!!!
2197 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2199 if(stringBuilder
== null){
2200 stringBuilder
= new StringBuilder();
2202 if(languages
== null || languages
.size() == 0){
2203 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2205 for(Language lang
: languages
){
2206 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2209 return stringBuilder
;
2213 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2214 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2215 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2217 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2220 UUID nameUuid
= taxon
.getName().getUuid();
2221 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2222 String epithetOfTaxon
= null;
2223 String infragenericEpithetOfTaxon
= null;
2224 String infraspecificEpithetOfTaxon
= null;
2225 if (taxonName
.isSpecies()){
2226 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2227 } else if (taxonName
.isInfraGeneric()){
2228 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2229 } else if (taxonName
.isInfraSpecific()){
2230 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2232 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2233 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2234 List
<String
> taxonNames
= new ArrayList
<String
>();
2236 for (TaxonNode node
: nodes
){
2237 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2238 // List<String> synonymsEpithet = new ArrayList<String>();
2240 if (node
.getClassification().equals(classification
)){
2241 if (!node
.isTopmostNode()){
2242 TaxonNode parent
= node
.getParent();
2243 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2244 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2245 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2246 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2247 Rank rankOfTaxon
= taxonName
.getRank();
2250 //create inferred synonyms for species, subspecies
2251 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2253 Synonym inferredEpithet
= null;
2254 Synonym inferredGenus
= null;
2255 Synonym potentialCombination
= null;
2257 List
<String
> propertyPaths
= new ArrayList
<String
>();
2258 propertyPaths
.add("synonym");
2259 propertyPaths
.add("synonym.name");
2260 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2261 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2263 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2264 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2266 List
<TaxonRelationship
> taxonRelListParent
= null;
2267 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2268 if (doWithMisappliedNames
){
2269 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2270 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2274 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2277 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2278 Synonym syn
= synonymRelationOfParent
.getSynonym();
2280 inferredEpithet
= createInferredEpithets(taxon
,
2281 zooHashMap
, taxonName
, epithetOfTaxon
,
2282 infragenericEpithetOfTaxon
,
2283 infraspecificEpithetOfTaxon
,
2284 taxonNames
, parentName
,
2288 inferredSynonyms
.add(inferredEpithet
);
2289 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2290 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2293 if (doWithMisappliedNames
){
2295 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2296 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2298 inferredEpithet
= createInferredEpithets(taxon
,
2299 zooHashMap
, taxonName
, epithetOfTaxon
,
2300 infragenericEpithetOfTaxon
,
2301 infraspecificEpithetOfTaxon
,
2302 taxonNames
, parentName
,
2305 inferredSynonyms
.add(inferredEpithet
);
2306 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2307 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2311 if (!taxonNames
.isEmpty()){
2312 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2313 ZoologicalName name
;
2314 if (!synNotInCDM
.isEmpty()){
2315 inferredSynonymsToBeRemoved
.clear();
2317 for (Synonym syn
:inferredSynonyms
){
2318 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2319 if (!synNotInCDM
.contains(name
.getNameCache())){
2320 inferredSynonymsToBeRemoved
.add(syn
);
2324 // Remove identified Synonyms from inferredSynonyms
2325 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2326 inferredSynonyms
.remove(synonym
);
2331 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2334 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2335 TaxonNameBase synName
;
2336 ZoologicalName inferredSynName
;
2338 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2339 inferredGenus
= createInferredGenus(taxon
,
2340 zooHashMap
, taxonName
, epithetOfTaxon
,
2341 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2343 inferredSynonyms
.add(inferredGenus
);
2344 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2345 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2350 if (doWithMisappliedNames
){
2352 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2353 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2354 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2356 inferredSynonyms
.add(inferredGenus
);
2357 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2358 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2363 if (!taxonNames
.isEmpty()){
2364 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2365 ZoologicalName name
;
2366 if (!synNotInCDM
.isEmpty()){
2367 inferredSynonymsToBeRemoved
.clear();
2369 for (Synonym syn
:inferredSynonyms
){
2370 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2371 if (!synNotInCDM
.contains(name
.getNameCache())){
2372 inferredSynonymsToBeRemoved
.add(syn
);
2376 // Remove identified Synonyms from inferredSynonyms
2377 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2378 inferredSynonyms
.remove(synonym
);
2383 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2385 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2386 ZoologicalName inferredSynName
;
2387 //for all synonyms of the parent...
2388 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2389 TaxonNameBase synName
;
2390 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2391 synName
= synParent
.getName();
2393 HibernateProxyHelper
.deproxy(synParent
);
2395 // Set the sourceReference
2396 sourceReference
= synParent
.getSec();
2398 // Determine the idInSource
2399 String idInSourceParent
= getIdInSource(synParent
);
2401 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2402 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2403 String synParentInfragenericName
= null;
2404 String synParentSpecificEpithet
= null;
2406 if (parentSynZooName
.isInfraGeneric()){
2407 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2409 if (parentSynZooName
.isSpecies()){
2410 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2413 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2414 synonymsGenus.put(synGenusName, idInSource);
2417 //for all synonyms of the taxon
2419 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2421 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2422 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2423 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2425 synParentInfragenericName
,
2426 synParentSpecificEpithet
, syn
, zooHashMap
);
2428 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2429 inferredSynonyms
.add(potentialCombination
);
2430 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2431 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2438 if (doWithMisappliedNames
){
2440 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2442 TaxonNameBase misappliedParentName
;
2444 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2445 misappliedParentName
= misappliedParent
.getName();
2447 HibernateProxyHelper
.deproxy(misappliedParent
);
2449 // Set the sourceReference
2450 sourceReference
= misappliedParent
.getSec();
2452 // Determine the idInSource
2453 String idInSourceParent
= getIdInSource(misappliedParent
);
2455 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2456 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2457 String synParentInfragenericName
= null;
2458 String synParentSpecificEpithet
= null;
2460 if (parentSynZooName
.isInfraGeneric()){
2461 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2463 if (parentSynZooName
.isSpecies()){
2464 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2468 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2469 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2470 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2471 potentialCombination
= createPotentialCombination(
2472 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2474 synParentInfragenericName
,
2475 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2478 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2479 inferredSynonyms
.add(potentialCombination
);
2480 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2481 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2486 if (!taxonNames
.isEmpty()){
2487 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2488 ZoologicalName name
;
2489 if (!synNotInCDM
.isEmpty()){
2490 inferredSynonymsToBeRemoved
.clear();
2491 for (Synonym syn
:inferredSynonyms
){
2493 name
= (ZoologicalName
) syn
.getName();
2494 }catch (ClassCastException e
){
2495 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2497 if (!synNotInCDM
.contains(name
.getNameCache())){
2498 inferredSynonymsToBeRemoved
.add(syn
);
2501 // Remove identified Synonyms from inferredSynonyms
2502 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2503 inferredSynonyms
.remove(synonym
);
2509 logger
.info("The synonymrelationship type is not defined.");
2510 return inferredSynonyms
;
2517 return inferredSynonyms
;
2520 private Synonym
createPotentialCombination(String idInSourceParent
,
2521 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2522 String synParentInfragenericName
, String synParentSpecificEpithet
,
2523 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2524 Synonym potentialCombination
;
2525 Reference sourceReference
;
2526 ZoologicalName inferredSynName
;
2527 HibernateProxyHelper
.deproxy(syn
);
2529 // Set sourceReference
2530 sourceReference
= syn
.getSec();
2531 if (sourceReference
== null){
2532 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2534 if (!parentSynZooName
.getTaxa().isEmpty()){
2535 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2537 sourceReference
= taxon
.getSec();
2540 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2542 String synTaxonInfraSpecificName
= null;
2544 if (parentSynZooName
.isSpecies()){
2545 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2548 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2549 synonymsEpithet.add(epithetName);
2552 //create potential combinations...
2553 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2555 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2556 if (zooSynName
.isSpecies()){
2557 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2558 if (parentSynZooName
.isInfraGeneric()){
2559 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2562 if (zooSynName
.isInfraSpecific()){
2563 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2564 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2566 if (parentSynZooName
.isInfraGeneric()){
2567 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2571 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2573 // Set the sourceReference
2574 potentialCombination
.setSec(sourceReference
);
2577 // Determine the idInSource
2578 String idInSourceSyn
= getIdInSource(syn
);
2580 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2581 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2582 inferredSynName
.addSource(originalSource
);
2583 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2584 potentialCombination
.addSource(originalSource
);
2587 inferredSynName
.generateTitle();
2589 return potentialCombination
;
2592 private Synonym
createInferredGenus(Taxon taxon
,
2593 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2594 String epithetOfTaxon
, String genusOfTaxon
,
2595 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2598 Synonym inferredGenus
;
2599 TaxonNameBase synName
;
2600 ZoologicalName inferredSynName
;
2601 synName
=syn
.getName();
2602 HibernateProxyHelper
.deproxy(syn
);
2604 // Determine the idInSource
2605 String idInSourceSyn
= getIdInSource(syn
);
2606 String idInSourceTaxon
= getIdInSource(taxon
);
2607 // Determine the sourceReference
2608 Reference sourceReference
= syn
.getSec();
2610 //logger.warn(sourceReference.getTitleCache());
2612 synName
= syn
.getName();
2613 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2614 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2615 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2616 synonymsEpithet.add(synSpeciesEpithetName);
2619 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2620 //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...
2623 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2624 if (zooParentName
.isInfraGeneric()){
2625 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2628 if (taxonName
.isSpecies()){
2629 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2631 if (taxonName
.isInfraSpecific()){
2632 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2633 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2637 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2639 // Set the sourceReference
2640 inferredGenus
.setSec(sourceReference
);
2642 // Add the original source
2643 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2644 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2645 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2646 inferredGenus
.addSource(originalSource
);
2648 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2649 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2650 inferredSynName
.addSource(originalSource
);
2651 originalSource
= null;
2654 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2655 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2656 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2657 inferredGenus
.addSource(originalSource
);
2659 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2660 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2661 inferredSynName
.addSource(originalSource
);
2662 originalSource
= null;
2665 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2667 inferredSynName
.generateTitle();
2670 return inferredGenus
;
2673 private Synonym
createInferredEpithets(Taxon taxon
,
2674 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2675 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2676 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2677 TaxonNameBase parentName
, TaxonBase syn
) {
2679 Synonym inferredEpithet
;
2680 TaxonNameBase
<?
,?
> synName
;
2681 ZoologicalName inferredSynName
;
2682 HibernateProxyHelper
.deproxy(syn
);
2684 // Determine the idInSource
2685 String idInSourceSyn
= getIdInSource(syn
);
2686 String idInSourceTaxon
= getIdInSource(taxon
);
2687 // Determine the sourceReference
2688 Reference
<?
> sourceReference
= syn
.getSec();
2690 if (sourceReference
== null){
2691 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2692 sourceReference
= taxon
.getSec();
2695 synName
= syn
.getName();
2696 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2697 String synGenusName
= zooSynName
.getGenusOrUninomial();
2698 String synInfraGenericEpithet
= null;
2699 String synSpecificEpithet
= null;
2701 if (zooSynName
.getInfraGenericEpithet() != null){
2702 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2705 if (zooSynName
.isInfraSpecific()){
2706 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2709 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2710 synonymsGenus.put(synGenusName, idInSource);
2713 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2715 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2716 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2717 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2719 inferredSynName
.setGenusOrUninomial(synGenusName
);
2721 if (parentName
.isInfraGeneric()){
2722 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2724 if (taxonName
.isSpecies()){
2725 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2726 }else if (taxonName
.isInfraSpecific()){
2727 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2728 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2731 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2733 // Set the sourceReference
2734 inferredEpithet
.setSec(sourceReference
);
2736 /* Add the original source
2737 if (idInSource != null) {
2738 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2741 Reference citation = getCitation(syn);
2742 if (citation != null) {
2743 originalSource.setCitation(citation);
2744 inferredEpithet.addSource(originalSource);
2747 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2750 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2751 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2753 inferredEpithet
.addSource(originalSource
);
2755 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2756 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2758 inferredSynName
.addSource(originalSource
);
2762 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2764 inferredSynName
.generateTitle();
2765 return inferredEpithet
;
2769 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2770 * Very likely only useful for createInferredSynonyms().
2775 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2776 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2777 if (taxonName
== null) {
2778 taxonName
= zooHashMap
.get(uuid
);
2784 * Returns the idInSource for a given Synonym.
2787 private String
getIdInSource(TaxonBase taxonBase
) {
2788 String idInSource
= null;
2789 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2790 if (sources
.size() == 1) {
2791 IdentifiableSource source
= sources
.iterator().next();
2792 if (source
!= null) {
2793 idInSource
= source
.getIdInSource();
2795 } else if (sources
.size() > 1) {
2798 for (IdentifiableSource source
: sources
) {
2799 idInSource
+= source
.getIdInSource();
2800 if (count
< sources
.size()) {
2805 } else if (sources
.size() == 0){
2806 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2815 * Returns the citation for a given Synonym.
2818 private Reference
getCitation(Synonym syn
) {
2819 Reference citation
= null;
2820 Set
<IdentifiableSource
> sources
= syn
.getSources();
2821 if (sources
.size() == 1) {
2822 IdentifiableSource source
= sources
.iterator().next();
2823 if (source
!= null) {
2824 citation
= source
.getCitation();
2826 } else if (sources
.size() > 1) {
2827 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2834 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2835 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2837 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2838 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2839 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2841 return inferredSynonyms
;
2845 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2848 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2850 // TODO quickly implemented, create according dao !!!!
2851 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2852 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2853 List
<Classification
> list
= new ArrayList
<Classification
>();
2855 if (taxonBase
== null) {
2859 taxonBase
= load(taxonBase
.getUuid());
2861 if (taxonBase
instanceof Taxon
) {
2862 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2864 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2865 nodes
.addAll(taxon
.getTaxonNodes());
2868 for (TaxonNode node
: nodes
) {
2869 classifications
.add(node
.getClassification());
2871 list
.addAll(classifications
);