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
.List
;
21 import java
.util
.UUID
;
23 import org
.apache
.log4j
.Logger
;
24 import org
.apache
.lucene
.index
.CorruptIndexException
;
25 import org
.apache
.lucene
.queryParser
.ParseException
;
26 import org
.apache
.lucene
.search
.BooleanClause
.Occur
;
27 import org
.apache
.lucene
.search
.BooleanFilter
;
28 import org
.apache
.lucene
.search
.BooleanQuery
;
29 import org
.apache
.lucene
.search
.DocIdSet
;
30 import org
.apache
.lucene
.search
.Query
;
31 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
32 import org
.apache
.lucene
.search
.SortField
;
33 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
34 import org
.springframework
.stereotype
.Service
;
35 import org
.springframework
.transaction
.annotation
.Transactional
;
37 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
40 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
41 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
42 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
43 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
45 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
46 import eu
.etaxonomy
.cdm
.api
.service
.search
.DocIdBitSetPrinter
;
47 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
53 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
54 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
55 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
56 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
57 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
58 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
59 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
60 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
61 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
62 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
63 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
64 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
65 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
66 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
67 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
68 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
69 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
70 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
71 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
72 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
73 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
74 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
75 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
76 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
77 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
78 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
79 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
80 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
81 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
82 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
83 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
84 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
85 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
86 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
87 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
88 import eu
.etaxonomy
.cdm
.model
.molecular
.Amplification
;
89 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
90 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
91 import eu
.etaxonomy
.cdm
.model
.molecular
.SingleRead
;
92 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
93 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
94 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
95 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
96 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
97 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
98 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
99 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
100 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
101 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
102 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
103 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
104 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
105 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
106 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
107 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
108 import eu
.etaxonomy
.cdm
.persistence
.dao
.AbstractBeanInitializer
;
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
.name
.ITaxonNameDao
;
112 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
113 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
114 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
115 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
116 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
117 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
118 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
122 * @author a.kohlbecker
127 @Transactional(readOnly
= true)
128 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
129 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
131 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
133 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
135 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
139 private ITaxonNameDao nameDao
;
142 private INameService nameService
;
145 private ICdmGenericDao genericDao
;
148 private IDescriptionService descriptionService
;
151 private IOrderedTermVocabularyDao orderedVocabularyDao
;
154 private IOccurrenceDao occurrenceDao
;
157 private AbstractBeanInitializer beanInitializer
;
160 private ILuceneIndexToolProvider luceneIndexToolProvider
;
166 public TaxonServiceImpl(){
167 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
171 * FIXME Candidate for harmonization
172 * rename searchByName ?
175 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
176 return dao
.getTaxaByName(name
, sec
);
180 * FIXME Candidate for harmonization
181 * list(Synonym.class, ...)
183 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
186 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
187 return dao
.getAllSynonyms(limit
, start
);
191 * FIXME Candidate for harmonization
192 * list(Taxon.class, ...)
194 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
197 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
198 return dao
.getAllTaxa(limit
, start
);
202 * FIXME Candidate for harmonization
203 * merge with getRootTaxa(Reference sec, ..., ...)
205 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
208 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
209 if (cdmFetch
== null){
210 cdmFetch
= CdmFetch
.NO_FETCH();
212 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
217 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
220 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
221 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
225 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
228 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
229 return dao
.getAllRelationships(limit
, start
);
233 * FIXME Candidate for harmonization
234 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
238 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
240 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
241 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
242 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
243 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
244 return taxonRelTypeVocabulary
;
251 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
254 @Transactional(readOnly
= false)
255 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
257 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
258 synonymName
.removeTaxonBase(synonym
);
259 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
260 taxonName
.removeTaxonBase(acceptedTaxon
);
262 synonym
.setName(taxonName
);
263 acceptedTaxon
.setName(synonymName
);
265 // the accepted taxon needs a new uuid because the concept has changed
266 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
267 //acceptedTaxon.setUuid(UUID.randomUUID());
272 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
274 //TODO correct delete handling still needs to be implemented / checked
276 @Transactional(readOnly
= false)
277 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
279 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
280 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
281 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
283 //check synonym is not homotypic
284 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
285 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
286 throw new HomotypicalGroupChangeException(message
);
289 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
291 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
292 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
294 for (Synonym heteroSynonym
: heteroSynonyms
){
295 if (synonym
.equals(heteroSynonym
)){
296 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
298 //move synonyms in same homotypic group to new accepted taxon
299 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
303 //synonym.getName().removeTaxonBase(synonym);
304 //TODO correct delete handling still needs to be implemented / checked
306 // deleteSynonym(synonym, taxon, false);
309 this.delete(synonym
);
311 } catch (Exception e
) {
312 logger
.info("Can't delete old synonym from database");
316 return newAcceptedTaxon
;
321 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
323 // Get name from synonym
324 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
326 // remove synonym from taxon
327 toTaxon
.removeSynonym(synonym
);
329 // Create a taxon with synonym name
330 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
332 // Add taxon relation
333 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
335 // since we are swapping names, we have to detach the name from the synonym completely.
336 // Otherwise the synonym will still be in the list of typified names.
337 synonym
.getName().removeTaxonBase(synonym
);
344 * @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)
346 @Transactional(readOnly
= false)
348 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
349 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
351 TaxonNameBase synonymName
= synonym
.getName();
352 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
356 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
357 newHomotypicalGroup
.addTypifiedName(synonymName
);
359 //remove existing basionym relationships
360 synonymName
.removeBasionyms();
362 //add basionym relationship
363 if (setBasionymRelationIfApplicable
){
364 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
365 for (TaxonNameBase basionym
: basionyms
){
366 synonymName
.addBasionym(basionym
);
370 //set synonym relationship correctly
371 // SynonymRelationship relToTaxon = null;
372 boolean relToTargetTaxonExists
= false;
373 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
374 for (SynonymRelationship rel
: existingRelations
){
375 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
376 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
377 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
378 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
379 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
380 rel
.setType(newRelationType
);
381 //TODO handle citation and microCitation
384 relToTargetTaxonExists
= true;
386 if (removeFromOtherTaxa
){
387 acceptedTaxon
.removeSynonym(synonym
, false);
393 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
394 Taxon acceptedTaxon
= targetTaxon
;
395 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
396 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
397 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
398 //TODO handle citation and microCitation
399 Reference citation
= null;
400 String microCitation
= null;
401 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
408 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
411 @Transactional(readOnly
= false)
412 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
414 clazz
= TaxonBase
.class;
416 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
421 protected void setDao(ITaxonDao dao
) {
426 * @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)
429 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
,
430 String uninomial
, String infragenericEpithet
, String specificEpithet
,
431 String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
432 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
434 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
435 if(numberOfResults
> 0) { // no point checking again
436 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
439 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
444 * @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)
447 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
448 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
450 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
451 if(numberOfResults
> 0) { // no point checking again
452 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
459 * @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)
462 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
463 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
465 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
466 if(numberOfResults
> 0) { // no point checking again
467 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
473 * @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)
476 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
477 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
479 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
480 if(numberOfResults
> 0) { // no point checking again
481 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
483 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
487 * @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)
490 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
491 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
493 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
494 if(numberOfResults
> 0) { // no point checking again
495 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
501 * @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)
504 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
505 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
507 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
508 if(numberOfResults
> 0) { // no point checking again
509 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
511 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
516 * @param includeRelationships
520 * @param propertyPaths
521 * @return an List which is not specifically ordered
524 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
525 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
527 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
528 relatedTaxa
.remove(taxon
);
529 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
535 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
536 * <code>taxon</code> supplied as parameter.
539 * @param includeRelationships
541 * @param maxDepth can be <code>null</code> for infinite depth
544 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
550 if(maxDepth
!= null) {
553 if(logger
.isDebugEnabled()){
554 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
556 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
557 for (TaxonRelationship taxRel
: taxonRelationships
) {
560 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
563 // filter by includeRelationships
564 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
565 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
566 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
567 if(logger
.isDebugEnabled()){
568 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
570 taxa
.add(taxRel
.getToTaxon());
571 if(maxDepth
== null || maxDepth
> 0) {
572 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
575 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
576 taxa
.add(taxRel
.getFromTaxon());
577 if(logger
.isDebugEnabled()){
578 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
580 if(maxDepth
== null || maxDepth
> 0) {
581 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
591 * @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)
594 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
595 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
597 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
598 if(numberOfResults
> 0) { // no point checking again
599 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
602 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
606 * @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)
609 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
610 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
612 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
613 if(numberOfResults
> 0) { // no point checking again
614 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
617 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
621 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
624 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
625 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
626 return t
.getHomotypicSynonymsByHomotypicGroup();
630 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
633 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
634 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
635 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
636 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
637 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
638 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
640 return heterotypicSynonymyGroups
;
644 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
646 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
647 // Class<? extends TaxonBase> clazz = null;
648 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
649 // clazz = TaxonBase.class;
650 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
651 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
652 // } else if(configurator.isDoTaxa()) {
653 // clazz = Taxon.class;
654 // //propertyPath = configurator.getTaxonPropertyPath();
655 // } else if (configurator.isDoSynonyms()) {
656 // clazz = Synonym.class;
657 // //propertyPath = configurator.getSynonymPropertyPath();
661 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
666 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
669 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
671 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
672 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
673 List
<TaxonBase
> taxa
= null;
676 long numberTaxaResults
= 0L;
679 List
<String
> propertyPath
= new ArrayList
<String
>();
680 if(configurator
.getTaxonPropertyPath() != null){
681 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
685 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
686 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
688 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
689 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
690 configurator
.getNamedAreas());
693 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
694 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
695 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
696 configurator
.getMatchMode(), configurator
.getNamedAreas(),
697 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
701 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
704 results
.addAll(taxa
);
707 numberOfResults
+= numberTaxaResults
;
709 // Names without taxa
710 if (configurator
.isDoNamesWithoutTaxa()) {
711 int numberNameResults
= 0;
713 List
<?
extends TaxonNameBase
<?
,?
>> names
=
714 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
715 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
716 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
717 if (names
.size() > 0) {
718 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
719 if (taxonName
.getTaxonBases().size() == 0) {
720 results
.add(taxonName
);
724 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
725 numberOfResults
+= numberNameResults
;
729 // Taxa from common names
731 if (configurator
.isDoTaxaByCommonNames()) {
732 taxa
= new ArrayList
<TaxonBase
>();
733 numberTaxaResults
= 0;
734 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
735 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
737 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
738 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
739 for( Object
[] entry
: commonNameResults
) {
740 taxa
.add((TaxonBase
) entry
[0]);
744 results
.addAll(taxa
);
746 numberOfResults
+= numberTaxaResults
;
750 return new DefaultPagerImpl
<IdentifiableEntity
>
751 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
754 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
755 return dao
.getUuidAndTitleCache();
759 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
762 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
763 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
764 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
765 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
766 for (TaxonDescription taxDesc
: descriptions
){
767 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
768 for (DescriptionElementBase descElem
: elements
){
769 for(Media media
: descElem
.getMedia()){
771 //find the best matching representation
772 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
781 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
784 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
785 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
790 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
793 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
794 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
795 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
797 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
798 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
800 if (limitToGalleries
== null) {
801 limitToGalleries
= false;
804 // --- resolve related taxa
805 if (includeRelationships
!= null) {
806 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
809 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
811 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
812 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
813 // --- TaxonDescriptions
814 for (Taxon t
: taxa
) {
815 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
817 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
818 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
819 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
820 for (Media media
: element
.getMedia()) {
821 taxonMedia
.add(media
);
828 if(includeOccurrences
!= null && includeOccurrences
) {
829 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
831 for (Taxon t
: taxa
) {
832 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
834 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
836 // direct media removed from specimen #3597
837 // taxonMedia.addAll(occurrence.getMedia());
839 // SpecimenDescriptions
840 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
841 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
842 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
843 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
844 for (DescriptionElementBase element
: elements
) {
845 for (Media media
: element
.getMedia()) {
846 taxonMedia
.add(media
);
853 //TODO why may collections have media attached? #
854 if (occurrence
.isInstanceOf(DerivedUnit
.class)) {
855 DerivedUnit derivedUnit
= CdmBase
.deproxy(occurrence
, DerivedUnit
.class);
856 if (derivedUnit
.getCollection() != null){
857 taxonMedia
.addAll(derivedUnit
.getCollection().getMedia());
861 // pherograms & gelPhotos
862 if (occurrence
.isInstanceOf(DnaSample
.class)) {
863 DnaSample dnaSample
= CdmBase
.deproxy(occurrence
, DnaSample
.class);
864 Set
<Sequence
> sequences
= dnaSample
.getSequences();
865 //we do show only those gelPhotos which lead to a consensus sequence
866 for (Sequence sequence
: sequences
) {
867 Set
<Media
> dnaRelatedMedia
= new HashSet
<Media
>();
868 for (SingleRead singleRead
: sequence
.getSingleReads()){
869 Amplification amplification
= singleRead
.getAmplification();
870 dnaRelatedMedia
.add(amplification
.getGelPhoto());
871 dnaRelatedMedia
.add(singleRead
.getPherogram());
872 dnaRelatedMedia
.remove(null);
874 taxonMedia
.addAll(dnaRelatedMedia
);
881 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
882 // --- TaxonNameDescription
883 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
884 for (Taxon t
: taxa
) {
885 nameDescriptions
.addAll(t
.getName().getDescriptions());
887 for(TaxonNameDescription nameDescription
: nameDescriptions
){
888 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
889 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
890 for (DescriptionElementBase element
: elements
) {
891 for (Media media
: element
.getMedia()) {
892 taxonMedia
.add(media
);
899 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
904 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
907 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
908 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
912 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
915 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
916 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
920 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
923 public int countAllRelationships() {
924 return this.dao
.countAllRelationships();
931 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
934 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
935 return this.dao
.findIdenticalTaxonNames(propertyPath
);
940 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
943 public void deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
) throws ReferencedObjectUndeletableException
{
945 config
= new TaxonDeletionConfigurator();
949 if (! config
.isDeleteTaxonNodes()){
950 if (taxon
.getTaxonNodes().size() > 0){
951 String message
= "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
952 throw new ReferencedObjectUndeletableException(message
);
957 // SynonymRelationShip
958 if (config
.isDeleteSynonymRelations()){
959 boolean removeSynonymNameFromHomotypicalGroup
= false;
960 for (SynonymRelationship synRel
: taxon
.getSynonymRelations()){
961 Synonym synonym
= synRel
.getSynonym();
962 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
963 if (config
.isDeleteSynonymsIfPossible()){
965 boolean newHomotypicGroupIfNeeded
= true;
966 deleteSynonym(synonym
, taxon
, config
.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded
);
968 deleteSynonymRelationships(synonym
, taxon
);
974 if (! config
.isDeleteTaxonRelationships()){
975 if (taxon
.getTaxonRelations().size() > 0){
976 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.";
977 throw new ReferencedObjectUndeletableException(message
);
983 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
985 for (TaxonDescription desc
: descriptions
){
986 if (config
.isDeleteDescriptions()){
987 //TODO use description delete configurator ?
988 //FIXME check if description is ALWAYS deletable
989 descriptionService
.delete(desc
);
991 if (desc
.getDescribedSpecimenOrObservation() != null){
992 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
993 " which also describes specimens or abservations";
994 throw new ReferencedObjectUndeletableException(message
);
1000 //check references with only reverse mapping
1001 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
1002 for (CdmBase referencingObject
: referencingObjects
){
1003 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1004 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
1005 String message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
1006 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnit
.class).getTitleCache());
1007 throw new ReferencedObjectUndeletableException(message
);
1012 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
1013 String message
= "Taxon can't be deleted as it is used in polytomous key node";
1014 throw new ReferencedObjectUndeletableException(message
);
1018 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1019 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1020 throw new ReferencedObjectUndeletableException(message
);
1026 if (config
.isDeleteNameIfPossible()){
1028 nameService
.delete(taxon
.getName(), config
.getNameDeletionConfig());
1029 } catch (ReferencedObjectUndeletableException e
) {
1031 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1038 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1040 @Transactional(readOnly
= false)
1042 public void deleteSynonym(Synonym synonym
, Taxon taxon
, boolean removeNameIfPossible
,boolean newHomotypicGroupIfNeeded
) {
1043 if (synonym
== null){
1046 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1048 //remove synonymRelationship
1049 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1051 taxonSet
.add(taxon
);
1053 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1055 for (Taxon relatedTaxon
: taxonSet
){
1056 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1057 relatedTaxon
.removeSynonym(synonym
, newHomotypicGroupIfNeeded
);
1059 this.saveOrUpdate(synonym
);
1061 //TODO remove name from homotypical group?
1063 //remove synonym (if necessary)
1064 if (synonym
.getSynonymRelations().isEmpty()){
1065 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1066 synonym
.setName(null);
1067 dao
.delete(synonym
);
1069 //remove name if possible (and required)
1070 if (name
!= null && removeNameIfPossible
){
1072 nameService
.delete(name
, new NameDeletionConfigurator());
1073 }catch (DataChangeNoRollbackException ex
){
1074 if (logger
.isDebugEnabled()) {
1075 logger
.debug("Name wasn't deleted as it is referenced");
1084 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1087 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1089 return this.dao
.findIdenticalNamesNew(propertyPath
);
1093 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1096 public String
getPhylumName(TaxonNameBase name
){
1097 return this.dao
.getPhylumName(name
);
1101 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1104 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1105 return dao
.deleteSynonymRelationships(syn
, taxon
);
1109 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1112 public long deleteSynonymRelationships(Synonym syn
) {
1113 return dao
.deleteSynonymRelationships(syn
, null);
1118 * @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)
1121 public List
<SynonymRelationship
> listSynonymRelationships(
1122 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1123 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1124 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1126 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1127 if(numberOfResults
> 0) { // no point checking again
1128 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1134 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1137 public Taxon
findBestMatchingTaxon(String taxonName
) {
1138 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1139 config
.setTaxonNameTitle(taxonName
);
1140 return findBestMatchingTaxon(config
);
1146 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1148 Taxon bestCandidate
= null;
1150 // 1. search for acceptet taxa
1151 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1152 boolean bestCandidateMatchesSecUuid
= false;
1153 boolean bestCandidateIsInClassification
= false;
1154 int countEqualCandidates
= 0;
1155 for(TaxonBase taxonBaseCandidate
: taxonList
){
1156 if(taxonBaseCandidate
instanceof Taxon
){
1157 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1158 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1159 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1161 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1162 bestCandidate
= newCanditate
;
1163 countEqualCandidates
= 1;
1164 bestCandidateMatchesSecUuid
= true;
1168 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1169 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1171 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1172 bestCandidate
= newCanditate
;
1173 countEqualCandidates
= 1;
1174 bestCandidateIsInClassification
= true;
1177 if (bestCandidate
== null){
1178 bestCandidate
= newCanditate
;
1179 countEqualCandidates
= 1;
1183 }else{ //not Taxon.class
1186 countEqualCandidates
++;
1189 if (bestCandidate
!= null){
1190 if(countEqualCandidates
> 1){
1191 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1192 return bestCandidate
;
1194 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1195 return bestCandidate
;
1200 // 2. search for synonyms
1201 if (config
.isIncludeSynonyms()){
1202 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1203 for(TaxonBase taxonBase
: synonymList
){
1204 if(taxonBase
instanceof Synonym
){
1205 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1206 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1207 if(!acceptetdCandidates
.isEmpty()){
1208 bestCandidate
= acceptetdCandidates
.iterator().next();
1209 if(acceptetdCandidates
.size() == 1){
1210 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1211 return bestCandidate
;
1213 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1214 return bestCandidate
;
1216 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1222 } catch (Exception e
){
1224 e
.printStackTrace();
1227 return bestCandidate
;
1230 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1231 UUID configClassificationUuid
= config
.getClassificationUuid();
1232 if (configClassificationUuid
== null){
1235 for (TaxonNode node
: taxon
.getTaxonNodes()){
1236 UUID classUuid
= node
.getClassification().getUuid();
1237 if (configClassificationUuid
.equals(classUuid
)){
1244 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1245 UUID configSecUuid
= config
.getSecUuid();
1246 if (configSecUuid
== null){
1249 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1250 return configSecUuid
.equals(taxonSecUuid
);
1254 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1257 public Synonym
findBestMatchingSynonym(String taxonName
) {
1258 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1259 if(! synonymList
.isEmpty()){
1260 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1261 if(synonymList
.size() == 1){
1262 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1265 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1274 * @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)
1277 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1278 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1280 Synonym synonym
= oldSynonymRelation
.getSynonym();
1281 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1282 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1283 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1284 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1285 //set default relationship type
1286 if (newSynonymRelationshipType
== null){
1287 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1289 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1291 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1292 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1293 boolean isSingleInGroup
= !(hgSize
> 1);
1295 if (! isSingleInGroup
){
1296 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1297 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1298 if (isHomotypicToAccepted
){
1299 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.";
1300 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1301 message
= String
.format(message
, homotypicRelatives
);
1302 throw new HomotypicalGroupChangeException(message
);
1304 if (! moveHomotypicGroup
){
1305 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.";
1306 throw new HomotypicalGroupChangeException(message
);
1309 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1311 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1313 SynonymRelationship result
= null;
1314 //move all synonyms to new taxon
1315 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1316 for (Synonym syn
: homotypicSynonyms
){
1317 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1318 for (SynonymRelationship synRelation
: synRelations
){
1319 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1320 Reference
<?
> newReference
= reference
;
1321 if (newReference
== null && keepReference
){
1322 newReference
= synRelation
.getCitation();
1324 String newRefDetail
= referenceDetail
;
1325 if (newRefDetail
== null && keepReference
){
1326 newRefDetail
= synRelation
.getCitationMicroReference();
1328 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1329 fromTaxon
.removeSynonymRelation(synRelation
, false);
1331 //change homotypic group of synonym if relType is 'homotypic'
1332 // if (newRelTypeIsHomotypic){
1333 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1336 if (synRelation
.equals(oldSynonymRelation
)){
1337 result
= newSynRelation
;
1343 saveOrUpdate(newTaxon
);
1344 //Assert that there is a result
1345 if (result
== null){
1346 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1347 throw new IllegalStateException(message
);
1353 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1356 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1357 return dao
.getUuidAndTitleCacheTaxon();
1361 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1364 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1365 return dao
.getUuidAndTitleCacheSynonym();
1369 * @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)
1372 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1373 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1374 Classification classification
, List
<Language
> languages
,
1375 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1378 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
);
1380 // --- execute search
1381 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1383 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1384 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1386 // --- initialize taxa, thighlight matches ....
1387 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1388 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1389 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1391 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1392 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1396 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1397 Classification classification
,
1398 Integer pageSize
, Integer pageNumber
,
1399 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1401 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1403 // --- execute search
1404 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1406 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1407 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1409 // --- initialize taxa, thighlight matches ....
1410 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1411 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1412 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1414 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1415 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1420 * @param queryString
1421 * @param classification
1423 * @param highlightFragments
1424 * @param directorySelectClass
1427 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1428 boolean highlightFragments
) {
1429 BooleanQuery finalQuery
= new BooleanQuery();
1430 BooleanQuery textQuery
= new BooleanQuery();
1432 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1433 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1435 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1436 luceneSearch
.setSortFields(sortFields
);
1438 // ---- search criteria
1439 luceneSearch
.setCdmTypRestriction(clazz
);
1441 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1442 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1444 finalQuery
.add(textQuery
, Occur
.MUST
);
1446 if(classification
!= null){
1447 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1449 luceneSearch
.setQuery(finalQuery
);
1451 if(highlightFragments
){
1452 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1454 return luceneSearch
;
1458 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1459 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1460 * drawback of requiring to do the join an indexing time.
1461 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1463 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1465 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1466 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1469 * @param queryString
1470 * @param classification
1472 * @param highlightFragments
1474 * @throws IOException
1476 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1477 boolean highlightFragments
) throws IOException
{
1480 String queryTermField
;
1481 String toField
= "id"; // TaxonBase.uuid
1483 if(edge
.isBidirectional()){
1484 throw new RuntimeException("Bidirectional joining not supported!");
1487 fromField
= "relatedFrom.id";
1488 queryTermField
= "relatedFrom.titleCache";
1489 } else if(edge
.isInvers()) {
1490 fromField
= "relatedTo.id";
1491 queryTermField
= "relatedTo.titleCache";
1493 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1496 BooleanQuery finalQuery
= new BooleanQuery();
1498 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonBase
.class);
1499 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1501 BooleanQuery joinFromQuery
= new BooleanQuery();
1502 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1503 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1504 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1506 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1507 luceneSearch
.setSortFields(sortFields
);
1509 finalQuery
.add(joinQuery
, Occur
.MUST
);
1511 if(classification
!= null){
1512 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1514 luceneSearch
.setQuery(finalQuery
);
1516 if(highlightFragments
){
1517 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1519 return luceneSearch
;
1526 * @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)
1529 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1530 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1531 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
,
1532 boolean highlightFragments
, Integer pageSize
,
1533 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1534 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1536 if(highlightFragments
){
1537 logger
.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1538 "currently not fully supported by this method and thus " +
1539 "may not work with common names and misapplied names.");
1542 // convert sets to lists
1543 List
<NamedArea
> namedAreaList
= null;
1544 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1545 if(namedAreas
!= null){
1546 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1547 namedAreaList
.addAll(namedAreas
);
1549 if(distributionStatus
!= null){
1550 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1551 distributionStatusList
.addAll(distributionStatus
);
1554 // set default if parameter is null
1555 if(searchModes
== null){
1556 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1559 boolean addDistributionFilter
= namedAreas
!= null && namedAreas
.size() > 0;
1561 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1562 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1565 ======== filtering by distribution , HOWTO ========
1567 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1568 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1569 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1570 which will be put into a FilteredQuersy in the end ?
1573 3. how does it work in spatial?
1575 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1576 - http://www.infoq.com/articles/LuceneSpatialSupport
1577 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1578 ------------------------------------------------------------------------
1581 A) use a separate distribution filter per index sub-query/search:
1582 - byTaxonSyonym (query TaxaonBase):
1583 use a join area filter (Distribution -> TaxonBase)
1584 - byCommonName (query DescriptionElementBase): use an area filter on
1585 DescriptionElementBase !!! PROBLEM !!!
1586 This cannot work since the distributions are different entities than the
1587 common names and thus these are different lucene documents.
1588 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1589 use a join area filter (Distribution -> TaxonBase)
1591 B) use a common distribution filter for all index sub-query/searches:
1592 - use a common join area filter (Distribution -> TaxonBase)
1593 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1594 PROBLEM in this case: we are losing the fragment highlighting for the
1595 common names, since the returned documents are always TaxonBases
1598 /* The QueryFactory for creating filter queries on Distributions should
1599 * The query factory used for the common names query cannot be reused
1600 * for this case, since we want to only record the text fields which are
1601 * actually used in the primary query
1603 QueryFactory distributionFilterQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1605 BooleanFilter multiIndexByAreaFilter
= new BooleanFilter();
1608 // search for taxa or synonyms
1609 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1610 Class taxonBaseSubclass
= TaxonBase
.class;
1611 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1612 taxonBaseSubclass
= Taxon
.class;
1613 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1614 taxonBaseSubclass
= Synonym
.class;
1616 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
));
1617 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1618 /* A) does not work!!!!
1619 if(addDistributionFilter){
1620 // in this case we need a filter which uses a join query
1621 // to get the TaxonBase documents for the DescriptionElementBase documents
1622 // which are matching the areas in question
1623 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1625 distributionStatusList,
1626 distributionFilterQueryFactory
1628 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1631 if(addDistributionFilter
&& searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1632 // add additional area filter for synonyms
1633 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1634 String toField
= "accTaxon.id"; // id in TaxonBase index
1636 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1638 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1639 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1644 // search by CommonTaxonName
1645 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1647 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1648 Query byCommonNameJoinQuery
= descriptionElementQueryFactory
.newJoinQuery(
1649 "inDescription.taxon.id",
1651 QueryFactory
.addTypeRestriction(
1652 createByDescriptionElementFullTextQuery(queryString
, classification
, null, languages
, descriptionElementQueryFactory
)
1653 , CommonTaxonName
.class
1655 CommonTaxonName
.class);
1656 logger
.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery
.toString());
1657 LuceneSearch byCommonNameSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1658 byCommonNameSearch
.setCdmTypRestriction(Taxon
.class);
1659 byCommonNameSearch
.setQuery(byCommonNameJoinQuery
);
1660 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1662 luceneSearches
.add(byCommonNameSearch
);
1664 /* A) does not work!!!!
1666 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1667 queryString, classification, null, languages, highlightFragments)
1669 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1670 if(addDistributionFilter){
1671 // in this case we are able to use DescriptionElementBase documents
1672 // which are matching the areas in question directly
1673 BooleanQuery byDistributionQuery = createByDistributionQuery(
1675 distributionStatusList,
1676 distributionFilterQueryFactory
1678 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
1682 // search by misapplied names
1683 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1685 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1686 // which allows doing query time joins
1687 // finds the misapplied name (Taxon B) which is an misapplication for
1688 // a related Taxon A.
1690 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1691 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1692 queryString
, classification
, languages
, highlightFragments
));
1693 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1695 if(addDistributionFilter
){
1696 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1699 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
1700 * Maybe this is a but in java itself java.
1702 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
1705 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
1707 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
1708 * will execute as expected:
1710 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1711 * String toField = "relation." + misappliedNameForUuid +".to.id";
1713 * Comparing both strings by the String.equals method returns true, so both String are identical.
1715 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
1716 * 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)
1717 * The bug is persistent after a reboot of the development computer.
1719 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
1720 // String toField = "relation." + misappliedNameForUuid +".to.id";
1721 String toField
= "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
1722 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
1723 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
1725 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, distributionFilterQueryFactory
);
1726 Query taxonAreaJoinQuery
= distributionFilterQueryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1727 QueryWrapperFilter filter
= new QueryWrapperFilter(taxonAreaJoinQuery
);
1729 // debug code for bug described above
1730 DocIdSet filterMatchSet
= filter
.getDocIdSet(luceneIndexToolProvider
.getIndexReaderFor(Taxon
.class));
1731 System
.err
.println(DocIdBitSetPrinter
.docsAsString(filterMatchSet
, 100));
1733 multiIndexByAreaFilter
.add(filter
, Occur
.SHOULD
);
1737 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
,
1738 luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1741 if(addDistributionFilter
){
1744 // in this case we need a filter which uses a join query
1745 // to get the TaxonBase documents for the DescriptionElementBase documents
1746 // which are matching the areas in question
1748 // for toTaxa, doByCommonName
1749 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(
1751 distributionStatusList
,
1752 distributionFilterQueryFactory
1754 multiIndexByAreaFilter
.add(new QueryWrapperFilter(taxonAreaJoinQuery
), Occur
.SHOULD
);
1757 if (addDistributionFilter
){
1758 multiSearch
.setFilter(multiIndexByAreaFilter
);
1760 // --- execute search
1761 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1763 // --- initialize taxa, highlight matches ....
1764 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1767 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1768 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1770 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1771 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1775 * @param namedAreaList at least one area must be in the list
1776 * @param distributionStatusList optional
1778 * @throws IOException
1780 protected Query
createByDistributionJoinQuery(
1781 List
<NamedArea
> namedAreaList
,
1782 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1783 QueryFactory queryFactory
1784 ) throws IOException
{
1786 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1787 String toField
= "id"; // id in TaxonBase index
1789 BooleanQuery byDistributionQuery
= createByDistributionQuery(namedAreaList
, distributionStatusList
, queryFactory
);
1791 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, byDistributionQuery
, Distribution
.class);
1793 return taxonAreaJoinQuery
;
1797 * @param namedAreaList
1798 * @param distributionStatusList
1799 * @param queryFactory
1802 private BooleanQuery
createByDistributionQuery(List
<NamedArea
> namedAreaList
,
1803 List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
, QueryFactory queryFactory
) {
1804 BooleanQuery areaQuery
= new BooleanQuery();
1805 // area field from Distribution
1806 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
1808 // status field from Distribution
1809 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
1810 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
1813 logger
.debug("createByDistributionQuery() query: " + areaQuery
.toString());
1818 * This method has been primarily created for testing the area join query but might
1819 * also be useful in other situations
1821 * @param namedAreaList
1822 * @param distributionStatusList
1823 * @param classification
1824 * @param highlightFragments
1826 * @throws IOException
1828 protected LuceneSearch
prepareByDistributionSearch(
1829 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1830 Classification classification
) throws IOException
{
1832 BooleanQuery finalQuery
= new BooleanQuery();
1834 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1836 // FIXME is this query factory using the wrong type?
1837 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
1839 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1840 luceneSearch
.setSortFields(sortFields
);
1843 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
1845 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
1847 if(classification
!= null){
1848 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1851 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
1852 luceneSearch
.setQuery(finalQuery
);
1854 return luceneSearch
;
1860 * @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)
1863 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
1864 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
1865 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
1866 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1869 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
1871 // --- execute search
1872 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1874 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1875 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1877 // --- initialize taxa, highlight matches ....
1878 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1879 @SuppressWarnings("rawtypes")
1880 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1881 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1883 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1884 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1890 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
1891 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
1892 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1894 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
1895 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
);
1897 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
1899 // --- execute search
1900 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1902 // --- initialize taxa, highlight matches ....
1903 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1905 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1906 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1907 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1909 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1910 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1912 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1913 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1920 * @param queryString
1921 * @param classification
1924 * @param highlightFragments
1925 * @param directorySelectClass
1928 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
1929 String queryString
, Classification classification
, List
<Feature
> features
,
1930 List
<Language
> languages
, boolean highlightFragments
) {
1932 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
1933 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1935 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
1937 BooleanQuery finalQuery
= createByDescriptionElementFullTextQuery(queryString
, classification
, features
,
1938 languages
, descriptionElementQueryFactory
);
1940 luceneSearch
.setSortFields(sortFields
);
1941 luceneSearch
.setCdmTypRestriction(clazz
);
1942 luceneSearch
.setQuery(finalQuery
);
1943 if(highlightFragments
){
1944 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
1947 return luceneSearch
;
1951 * @param queryString
1952 * @param classification
1955 * @param descriptionElementQueryFactory
1958 private BooleanQuery
createByDescriptionElementFullTextQuery(String queryString
, Classification classification
,
1959 List
<Feature
> features
, List
<Language
> languages
, QueryFactory descriptionElementQueryFactory
) {
1960 BooleanQuery finalQuery
= new BooleanQuery();
1961 BooleanQuery textQuery
= new BooleanQuery();
1962 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1966 if(languages
== null || languages
.size() == 0){
1967 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
1969 nameQuery
= new BooleanQuery();
1970 BooleanQuery languageSubQuery
= new BooleanQuery();
1971 for(Language lang
: languages
){
1972 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
1974 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
1975 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
1977 textQuery
.add(nameQuery
, Occur
.SHOULD
);
1980 // text field from TextData
1981 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
1983 // --- TermBase fields - by representation ----
1984 // state field from CategoricalData
1985 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.state", queryString
, languages
), Occur
.SHOULD
);
1987 // state field from CategoricalData
1988 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("stateData.modifyingText", queryString
, languages
), Occur
.SHOULD
);
1990 // area field from Distribution
1991 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
1993 // status field from Distribution
1994 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
1996 finalQuery
.add(textQuery
, Occur
.MUST
);
1997 // --- classification ----
1999 if(classification
!= null){
2000 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
2003 // --- IdentifieableEntity fields - by uuid
2004 if(features
!= null && features
.size() > 0 ){
2005 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
2008 // the description must be associated with a taxon
2009 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
2011 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
2016 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2017 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2018 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2020 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2021 * or {@link MultilanguageTextFieldBridge }
2022 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2023 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2024 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2026 * TODO move to utiliy class !!!!!!!!
2028 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
2030 if(stringBuilder
== null){
2031 stringBuilder
= new StringBuilder();
2033 if(languages
== null || languages
.size() == 0){
2034 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
2036 for(Language lang
: languages
){
2037 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
2040 return stringBuilder
;
2044 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
2045 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2046 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
2048 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
2051 UUID nameUuid
= taxon
.getName().getUuid();
2052 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
2053 String epithetOfTaxon
= null;
2054 String infragenericEpithetOfTaxon
= null;
2055 String infraspecificEpithetOfTaxon
= null;
2056 if (taxonName
.isSpecies()){
2057 epithetOfTaxon
= taxonName
.getSpecificEpithet();
2058 } else if (taxonName
.isInfraGeneric()){
2059 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
2060 } else if (taxonName
.isInfraSpecific()){
2061 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
2063 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
2064 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
2065 List
<String
> taxonNames
= new ArrayList
<String
>();
2067 for (TaxonNode node
: nodes
){
2068 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2069 // List<String> synonymsEpithet = new ArrayList<String>();
2071 if (node
.getClassification().equals(classification
)){
2072 if (!node
.isTopmostNode()){
2073 TaxonNode parent
= node
.getParent();
2074 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
2075 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
2076 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
2077 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
2078 Rank rankOfTaxon
= taxonName
.getRank();
2081 //create inferred synonyms for species, subspecies
2082 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
2084 Synonym inferredEpithet
= null;
2085 Synonym inferredGenus
= null;
2086 Synonym potentialCombination
= null;
2088 List
<String
> propertyPaths
= new ArrayList
<String
>();
2089 propertyPaths
.add("synonym");
2090 propertyPaths
.add("synonym.name");
2091 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
2092 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
2094 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2095 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
2097 List
<TaxonRelationship
> taxonRelListParent
= null;
2098 List
<TaxonRelationship
> taxonRelListTaxon
= null;
2099 if (doWithMisappliedNames
){
2100 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2101 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
2105 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
2108 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2109 Synonym syn
= synonymRelationOfParent
.getSynonym();
2111 inferredEpithet
= createInferredEpithets(taxon
,
2112 zooHashMap
, taxonName
, epithetOfTaxon
,
2113 infragenericEpithetOfTaxon
,
2114 infraspecificEpithetOfTaxon
,
2115 taxonNames
, parentName
,
2119 inferredSynonyms
.add(inferredEpithet
);
2120 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2121 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2124 if (doWithMisappliedNames
){
2126 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
2127 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2129 inferredEpithet
= createInferredEpithets(taxon
,
2130 zooHashMap
, taxonName
, epithetOfTaxon
,
2131 infragenericEpithetOfTaxon
,
2132 infraspecificEpithetOfTaxon
,
2133 taxonNames
, parentName
,
2136 inferredSynonyms
.add(inferredEpithet
);
2137 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
2138 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
2142 if (!taxonNames
.isEmpty()){
2143 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2144 ZoologicalName name
;
2145 if (!synNotInCDM
.isEmpty()){
2146 inferredSynonymsToBeRemoved
.clear();
2148 for (Synonym syn
:inferredSynonyms
){
2149 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2150 if (!synNotInCDM
.contains(name
.getNameCache())){
2151 inferredSynonymsToBeRemoved
.add(syn
);
2155 // Remove identified Synonyms from inferredSynonyms
2156 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2157 inferredSynonyms
.remove(synonym
);
2162 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
2165 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2166 TaxonNameBase synName
;
2167 ZoologicalName inferredSynName
;
2169 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2170 inferredGenus
= createInferredGenus(taxon
,
2171 zooHashMap
, taxonName
, epithetOfTaxon
,
2172 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
2174 inferredSynonyms
.add(inferredGenus
);
2175 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2176 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2181 if (doWithMisappliedNames
){
2183 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2184 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2185 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
2187 inferredSynonyms
.add(inferredGenus
);
2188 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
2189 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
2194 if (!taxonNames
.isEmpty()){
2195 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2196 ZoologicalName name
;
2197 if (!synNotInCDM
.isEmpty()){
2198 inferredSynonymsToBeRemoved
.clear();
2200 for (Synonym syn
:inferredSynonyms
){
2201 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2202 if (!synNotInCDM
.contains(name
.getNameCache())){
2203 inferredSynonymsToBeRemoved
.add(syn
);
2207 // Remove identified Synonyms from inferredSynonyms
2208 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2209 inferredSynonyms
.remove(synonym
);
2214 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2216 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2217 ZoologicalName inferredSynName
;
2218 //for all synonyms of the parent...
2219 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2220 TaxonNameBase synName
;
2221 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2222 synName
= synParent
.getName();
2224 HibernateProxyHelper
.deproxy(synParent
);
2226 // Set the sourceReference
2227 sourceReference
= synParent
.getSec();
2229 // Determine the idInSource
2230 String idInSourceParent
= getIdInSource(synParent
);
2232 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2233 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2234 String synParentInfragenericName
= null;
2235 String synParentSpecificEpithet
= null;
2237 if (parentSynZooName
.isInfraGeneric()){
2238 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2240 if (parentSynZooName
.isSpecies()){
2241 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2244 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2245 synonymsGenus.put(synGenusName, idInSource);
2248 //for all synonyms of the taxon
2250 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2252 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2253 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2254 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2256 synParentInfragenericName
,
2257 synParentSpecificEpithet
, syn
, zooHashMap
);
2259 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2260 inferredSynonyms
.add(potentialCombination
);
2261 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2262 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2269 if (doWithMisappliedNames
){
2271 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2273 TaxonNameBase misappliedParentName
;
2275 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2276 misappliedParentName
= misappliedParent
.getName();
2278 HibernateProxyHelper
.deproxy(misappliedParent
);
2280 // Set the sourceReference
2281 sourceReference
= misappliedParent
.getSec();
2283 // Determine the idInSource
2284 String idInSourceParent
= getIdInSource(misappliedParent
);
2286 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2287 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2288 String synParentInfragenericName
= null;
2289 String synParentSpecificEpithet
= null;
2291 if (parentSynZooName
.isInfraGeneric()){
2292 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2294 if (parentSynZooName
.isSpecies()){
2295 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2299 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2300 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2301 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2302 potentialCombination
= createPotentialCombination(
2303 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2305 synParentInfragenericName
,
2306 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2309 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2310 inferredSynonyms
.add(potentialCombination
);
2311 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2312 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2317 if (!taxonNames
.isEmpty()){
2318 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2319 ZoologicalName name
;
2320 if (!synNotInCDM
.isEmpty()){
2321 inferredSynonymsToBeRemoved
.clear();
2322 for (Synonym syn
:inferredSynonyms
){
2324 name
= (ZoologicalName
) syn
.getName();
2325 }catch (ClassCastException e
){
2326 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2328 if (!synNotInCDM
.contains(name
.getNameCache())){
2329 inferredSynonymsToBeRemoved
.add(syn
);
2332 // Remove identified Synonyms from inferredSynonyms
2333 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2334 inferredSynonyms
.remove(synonym
);
2340 logger
.info("The synonymrelationship type is not defined.");
2341 return inferredSynonyms
;
2348 return inferredSynonyms
;
2351 private Synonym
createPotentialCombination(String idInSourceParent
,
2352 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2353 String synParentInfragenericName
, String synParentSpecificEpithet
,
2354 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2355 Synonym potentialCombination
;
2356 Reference sourceReference
;
2357 ZoologicalName inferredSynName
;
2358 HibernateProxyHelper
.deproxy(syn
);
2360 // Set sourceReference
2361 sourceReference
= syn
.getSec();
2362 if (sourceReference
== null){
2363 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2365 if (!parentSynZooName
.getTaxa().isEmpty()){
2366 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2368 sourceReference
= taxon
.getSec();
2371 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2373 String synTaxonInfraSpecificName
= null;
2375 if (parentSynZooName
.isSpecies()){
2376 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2379 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2380 synonymsEpithet.add(epithetName);
2383 //create potential combinations...
2384 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2386 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2387 if (zooSynName
.isSpecies()){
2388 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2389 if (parentSynZooName
.isInfraGeneric()){
2390 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2393 if (zooSynName
.isInfraSpecific()){
2394 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2395 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2397 if (parentSynZooName
.isInfraGeneric()){
2398 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2402 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2404 // Set the sourceReference
2405 potentialCombination
.setSec(sourceReference
);
2408 // Determine the idInSource
2409 String idInSourceSyn
= getIdInSource(syn
);
2411 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2412 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2413 inferredSynName
.addSource(originalSource
);
2414 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
, idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2415 potentialCombination
.addSource(originalSource
);
2418 inferredSynName
.generateTitle();
2420 return potentialCombination
;
2423 private Synonym
createInferredGenus(Taxon taxon
,
2424 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2425 String epithetOfTaxon
, String genusOfTaxon
,
2426 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2429 Synonym inferredGenus
;
2430 TaxonNameBase synName
;
2431 ZoologicalName inferredSynName
;
2432 synName
=syn
.getName();
2433 HibernateProxyHelper
.deproxy(syn
);
2435 // Determine the idInSource
2436 String idInSourceSyn
= getIdInSource(syn
);
2437 String idInSourceTaxon
= getIdInSource(taxon
);
2438 // Determine the sourceReference
2439 Reference sourceReference
= syn
.getSec();
2441 //logger.warn(sourceReference.getTitleCache());
2443 synName
= syn
.getName();
2444 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2445 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2446 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2447 synonymsEpithet.add(synSpeciesEpithetName);
2450 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2451 //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...
2454 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2455 if (zooParentName
.isInfraGeneric()){
2456 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2459 if (taxonName
.isSpecies()){
2460 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2462 if (taxonName
.isInfraSpecific()){
2463 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2464 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2468 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2470 // Set the sourceReference
2471 inferredGenus
.setSec(sourceReference
);
2473 // Add the original source
2474 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2475 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2476 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2477 inferredGenus
.addSource(originalSource
);
2479 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2480 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2481 inferredSynName
.addSource(originalSource
);
2482 originalSource
= null;
2485 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2486 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2487 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2488 inferredGenus
.addSource(originalSource
);
2490 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2491 idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2492 inferredSynName
.addSource(originalSource
);
2493 originalSource
= null;
2496 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2498 inferredSynName
.generateTitle();
2501 return inferredGenus
;
2504 private Synonym
createInferredEpithets(Taxon taxon
,
2505 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2506 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2507 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2508 TaxonNameBase parentName
, TaxonBase syn
) {
2510 Synonym inferredEpithet
;
2511 TaxonNameBase
<?
,?
> synName
;
2512 ZoologicalName inferredSynName
;
2513 HibernateProxyHelper
.deproxy(syn
);
2515 // Determine the idInSource
2516 String idInSourceSyn
= getIdInSource(syn
);
2517 String idInSourceTaxon
= getIdInSource(taxon
);
2518 // Determine the sourceReference
2519 Reference
<?
> sourceReference
= syn
.getSec();
2521 if (sourceReference
== null){
2522 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2523 sourceReference
= taxon
.getSec();
2526 synName
= syn
.getName();
2527 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2528 String synGenusName
= zooSynName
.getGenusOrUninomial();
2529 String synInfraGenericEpithet
= null;
2530 String synSpecificEpithet
= null;
2532 if (zooSynName
.getInfraGenericEpithet() != null){
2533 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2536 if (zooSynName
.isInfraSpecific()){
2537 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2540 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2541 synonymsGenus.put(synGenusName, idInSource);
2544 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2546 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2547 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2548 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2550 inferredSynName
.setGenusOrUninomial(synGenusName
);
2552 if (parentName
.isInfraGeneric()){
2553 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2555 if (taxonName
.isSpecies()){
2556 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2557 }else if (taxonName
.isInfraSpecific()){
2558 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2559 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2562 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2564 // Set the sourceReference
2565 inferredEpithet
.setSec(sourceReference
);
2567 /* Add the original source
2568 if (idInSource != null) {
2569 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2572 Reference citation = getCitation(syn);
2573 if (citation != null) {
2574 originalSource.setCitation(citation);
2575 inferredEpithet.addSource(originalSource);
2578 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2581 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2582 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2584 inferredEpithet
.addSource(originalSource
);
2586 originalSource
= IdentifiableSource
.NewInstance(OriginalSourceType
.Transformation
,
2587 taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2589 inferredSynName
.addSource(originalSource
);
2593 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2595 inferredSynName
.generateTitle();
2596 return inferredEpithet
;
2600 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2601 * Very likely only useful for createInferredSynonyms().
2606 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2607 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2608 if (taxonName
== null) {
2609 taxonName
= zooHashMap
.get(uuid
);
2615 * Returns the idInSource for a given Synonym.
2618 private String
getIdInSource(TaxonBase taxonBase
) {
2619 String idInSource
= null;
2620 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2621 if (sources
.size() == 1) {
2622 IdentifiableSource source
= sources
.iterator().next();
2623 if (source
!= null) {
2624 idInSource
= source
.getIdInSource();
2626 } else if (sources
.size() > 1) {
2629 for (IdentifiableSource source
: sources
) {
2630 idInSource
+= source
.getIdInSource();
2631 if (count
< sources
.size()) {
2636 } else if (sources
.size() == 0){
2637 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2646 * Returns the citation for a given Synonym.
2649 private Reference
getCitation(Synonym syn
) {
2650 Reference citation
= null;
2651 Set
<IdentifiableSource
> sources
= syn
.getSources();
2652 if (sources
.size() == 1) {
2653 IdentifiableSource source
= sources
.iterator().next();
2654 if (source
!= null) {
2655 citation
= source
.getCitation();
2657 } else if (sources
.size() > 1) {
2658 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2665 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2666 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2668 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2669 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2670 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2672 return inferredSynonyms
;
2676 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2679 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2681 // TODO quickly implemented, create according dao !!!!
2682 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2683 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2684 List
<Classification
> list
= new ArrayList
<Classification
>();
2686 if (taxonBase
== null) {
2690 taxonBase
= load(taxonBase
.getUuid());
2692 if (taxonBase
instanceof Taxon
) {
2693 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2695 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2696 nodes
.addAll(taxon
.getTaxonNodes());
2699 for (TaxonNode node
: nodes
) {
2700 classifications
.add(node
.getClassification());
2702 list
.addAll(classifications
);