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
.BooleanQuery
;
28 import org
.apache
.lucene
.search
.Query
;
29 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
30 import org
.apache
.lucene
.search
.SortField
;
31 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
32 import org
.springframework
.stereotype
.Service
;
33 import org
.springframework
.transaction
.annotation
.Transactional
;
35 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
36 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
37 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
40 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
41 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
42 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
44 import eu
.etaxonomy
.cdm
.api
.service
.search
.ILuceneIndexToolProvider
;
45 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
46 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearch
;
47 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneMultiSearchException
;
48 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
;
49 import eu
.etaxonomy
.cdm
.api
.service
.search
.LuceneSearch
.TopGroupsWithMaxScore
;
50 import eu
.etaxonomy
.cdm
.api
.service
.search
.QueryFactory
;
51 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
52 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResultBuilder
;
53 import eu
.etaxonomy
.cdm
.api
.service
.util
.TaxonRelationshipEdge
;
54 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
55 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
56 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
57 import eu
.etaxonomy
.cdm
.hibernate
.search
.GroupByTaxonClassBridge
;
58 import eu
.etaxonomy
.cdm
.hibernate
.search
.MultilanguageTextFieldBridge
;
59 import eu
.etaxonomy
.cdm
.model
.CdmBaseType
;
60 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
61 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
62 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
63 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
64 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
65 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
66 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
67 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
68 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
69 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
70 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
71 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
72 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
73 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
74 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
75 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
76 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
77 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
78 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
79 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
80 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
81 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
82 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
83 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
84 import eu
.etaxonomy
.cdm
.model
.molecular
.DnaSample
;
85 import eu
.etaxonomy
.cdm
.model
.molecular
.Sequence
;
86 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
87 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
88 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
89 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
90 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
91 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
92 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
93 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
94 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
95 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
96 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
97 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
98 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
99 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
100 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
101 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
102 import eu
.etaxonomy
.cdm
.persistence
.dao
.AbstractBeanInitializer
;
103 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
104 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
105 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
106 import eu
.etaxonomy
.cdm
.persistence
.dao
.occurrence
.IOccurrenceDao
;
107 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
108 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
109 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
110 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
111 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
112 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
116 * @author a.kohlbecker
121 @Transactional(readOnly
= true)
122 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
123 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
125 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
127 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
129 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
133 private ITaxonNameDao nameDao
;
136 private INameService nameService
;
139 private ICdmGenericDao genericDao
;
142 private IDescriptionService descriptionService
;
145 private IOrderedTermVocabularyDao orderedVocabularyDao
;
148 private IOccurrenceDao occurrenceDao
;
151 private AbstractBeanInitializer beanInitializer
;
154 private ILuceneIndexToolProvider luceneIndexToolProvider
;
160 public TaxonServiceImpl(){
161 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
165 * FIXME Candidate for harmonization
166 * rename searchByName ?
169 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
170 return dao
.getTaxaByName(name
, sec
);
174 * FIXME Candidate for harmonization
175 * list(Synonym.class, ...)
177 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
180 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
181 return dao
.getAllSynonyms(limit
, start
);
185 * FIXME Candidate for harmonization
186 * list(Taxon.class, ...)
188 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
191 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
192 return dao
.getAllTaxa(limit
, start
);
196 * FIXME Candidate for harmonization
197 * merge with getRootTaxa(Reference sec, ..., ...)
199 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
202 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
203 if (cdmFetch
== null){
204 cdmFetch
= CdmFetch
.NO_FETCH();
206 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
211 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
214 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
215 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
219 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
222 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
223 return dao
.getAllRelationships(limit
, start
);
227 * FIXME Candidate for harmonization
228 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
232 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
234 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
235 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
236 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
237 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
238 return taxonRelTypeVocabulary
;
245 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
248 @Transactional(readOnly
= false)
249 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
251 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
252 synonymName
.removeTaxonBase(synonym
);
253 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
254 taxonName
.removeTaxonBase(acceptedTaxon
);
256 synonym
.setName(taxonName
);
257 acceptedTaxon
.setName(synonymName
);
259 // the accepted taxon needs a new uuid because the concept has changed
260 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
261 //acceptedTaxon.setUuid(UUID.randomUUID());
266 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
268 //TODO correct delete handling still needs to be implemented / checked
270 @Transactional(readOnly
= false)
271 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
273 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
274 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
275 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
277 //check synonym is not homotypic
278 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
279 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
280 throw new HomotypicalGroupChangeException(message
);
283 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
285 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
286 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
288 for (Synonym heteroSynonym
: heteroSynonyms
){
289 if (synonym
.equals(heteroSynonym
)){
290 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
292 //move synonyms in same homotypic group to new accepted taxon
293 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
297 //synonym.getName().removeTaxonBase(synonym);
298 //TODO correct delete handling still needs to be implemented / checked
300 // deleteSynonym(synonym, taxon, false);
303 this.delete(synonym
);
305 } catch (Exception e
) {
306 logger
.info("Can't delete old synonym from database");
310 return newAcceptedTaxon
;
315 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
317 // Get name from synonym
318 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
320 // remove synonym from taxon
321 toTaxon
.removeSynonym(synonym
);
323 // Create a taxon with synonym name
324 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
326 // Add taxon relation
327 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
329 // since we are swapping names, we have to detach the name from the synonym completely.
330 // Otherwise the synonym will still be in the list of typified names.
331 synonym
.getName().removeTaxonBase(synonym
);
338 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeHomotypicalGroupOfSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.name.HomotypicalGroup, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
340 @Transactional(readOnly
= false)
342 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
343 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
345 TaxonNameBase synonymName
= synonym
.getName();
346 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
350 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
351 newHomotypicalGroup
.addTypifiedName(synonymName
);
353 //remove existing basionym relationships
354 synonymName
.removeBasionyms();
356 //add basionym relationship
357 if (setBasionymRelationIfApplicable
){
358 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
359 for (TaxonNameBase basionym
: basionyms
){
360 synonymName
.addBasionym(basionym
);
364 //set synonym relationship correctly
365 // SynonymRelationship relToTaxon = null;
366 boolean relToTargetTaxonExists
= false;
367 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
368 for (SynonymRelationship rel
: existingRelations
){
369 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
370 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
371 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
372 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
373 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
374 rel
.setType(newRelationType
);
375 //TODO handle citation and microCitation
378 relToTargetTaxonExists
= true;
380 if (removeFromOtherTaxa
){
381 acceptedTaxon
.removeSynonym(synonym
, false);
387 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
388 Taxon acceptedTaxon
= targetTaxon
;
389 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
390 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
391 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
392 //TODO handle citation and microCitation
393 Reference citation
= null;
394 String microCitation
= null;
395 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
402 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
405 @Transactional(readOnly
= false)
406 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
408 clazz
= TaxonBase
.class;
410 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
415 protected void setDao(ITaxonDao dao
) {
420 * @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)
423 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
,
424 String uninomial
, String infragenericEpithet
, String specificEpithet
,
425 String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
426 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
428 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
429 if(numberOfResults
> 0) { // no point checking again
430 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
433 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
438 * @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)
441 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
442 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
444 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
445 if(numberOfResults
> 0) { // no point checking again
446 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
453 * @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)
456 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
457 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
459 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
460 if(numberOfResults
> 0) { // no point checking again
461 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
467 * @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)
470 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
471 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
473 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
474 if(numberOfResults
> 0) { // no point checking again
475 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
477 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
481 * @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)
484 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
485 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
487 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
488 if(numberOfResults
> 0) { // no point checking again
489 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
495 * @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)
498 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
499 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
501 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
502 if(numberOfResults
> 0) { // no point checking again
503 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
505 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
510 * @param includeRelationships
514 * @param propertyPaths
515 * @return an List which is not specifically ordered
518 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
519 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
521 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
522 relatedTaxa
.remove(taxon
);
523 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
529 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
530 * <code>taxon</code> supplied as parameter.
533 * @param includeRelationships
535 * @param maxDepth can be <code>null</code> for infinite depth
538 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
544 if(maxDepth
!= null) {
547 if(logger
.isDebugEnabled()){
548 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
550 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
551 for (TaxonRelationship taxRel
: taxonRelationships
) {
554 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
557 // filter by includeRelationships
558 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
559 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
560 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
561 if(logger
.isDebugEnabled()){
562 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
564 taxa
.add(taxRel
.getToTaxon());
565 if(maxDepth
== null || maxDepth
> 0) {
566 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
569 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
570 taxa
.add(taxRel
.getFromTaxon());
571 if(logger
.isDebugEnabled()){
572 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
574 if(maxDepth
== null || maxDepth
> 0) {
575 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
585 * @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)
588 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
589 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
591 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
592 if(numberOfResults
> 0) { // no point checking again
593 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
596 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
600 * @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)
603 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
604 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
606 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
607 if(numberOfResults
> 0) { // no point checking again
608 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
611 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
615 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
618 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
619 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
620 return t
.getHomotypicSynonymsByHomotypicGroup();
624 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
627 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
628 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
629 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
630 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
631 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
632 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
634 return heterotypicSynonymyGroups
;
638 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
640 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
641 // Class<? extends TaxonBase> clazz = null;
642 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
643 // clazz = TaxonBase.class;
644 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
645 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
646 // } else if(configurator.isDoTaxa()) {
647 // clazz = Taxon.class;
648 // //propertyPath = configurator.getTaxonPropertyPath();
649 // } else if (configurator.isDoSynonyms()) {
650 // clazz = Synonym.class;
651 // //propertyPath = configurator.getSynonymPropertyPath();
655 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
660 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
663 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
665 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
666 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
667 List
<TaxonBase
> taxa
= null;
670 long numberTaxaResults
= 0L;
673 List
<String
> propertyPath
= new ArrayList
<String
>();
674 if(configurator
.getTaxonPropertyPath() != null){
675 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
679 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
680 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
682 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
683 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
684 configurator
.getNamedAreas());
687 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
688 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
689 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
690 configurator
.getMatchMode(), configurator
.getNamedAreas(),
691 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
695 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
698 results
.addAll(taxa
);
701 numberOfResults
+= numberTaxaResults
;
703 // Names without taxa
704 if (configurator
.isDoNamesWithoutTaxa()) {
705 int numberNameResults
= 0;
707 List
<?
extends TaxonNameBase
<?
,?
>> names
=
708 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
709 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
710 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
711 if (names
.size() > 0) {
712 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
713 if (taxonName
.getTaxonBases().size() == 0) {
714 results
.add(taxonName
);
718 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
719 numberOfResults
+= numberNameResults
;
723 // Taxa from common names
725 if (configurator
.isDoTaxaByCommonNames()) {
726 taxa
= new ArrayList
<TaxonBase
>();
727 numberTaxaResults
= 0;
728 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
729 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
731 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
732 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
733 for( Object
[] entry
: commonNameResults
) {
734 taxa
.add((TaxonBase
) entry
[0]);
738 results
.addAll(taxa
);
740 numberOfResults
+= numberTaxaResults
;
744 return new DefaultPagerImpl
<IdentifiableEntity
>
745 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
748 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
749 return dao
.getUuidAndTitleCache();
753 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
756 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
757 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
758 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
759 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
760 for (TaxonDescription taxDesc
: descriptions
){
761 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
762 for (DescriptionElementBase descElem
: elements
){
763 for(Media media
: descElem
.getMedia()){
765 //find the best matching representation
766 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
775 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
778 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
779 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
784 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
787 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
788 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
789 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
791 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
792 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
794 if (limitToGalleries
== null) {
795 limitToGalleries
= false;
798 // --- resolve related taxa
799 if (includeRelationships
!= null) {
800 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
803 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
805 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
806 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
807 // --- TaxonDescriptions
808 for (Taxon t
: taxa
) {
809 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
811 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
812 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
813 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
814 for (Media media
: element
.getMedia()) {
815 taxonMedia
.add(media
);
822 if(includeOccurrences
!= null && includeOccurrences
) {
823 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
825 for (Taxon t
: taxa
) {
826 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
828 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
830 taxonMedia
.addAll(occurrence
.getMedia());
832 // SpecimenDescriptions
833 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
834 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
835 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
836 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
837 for (DescriptionElementBase element
: elements
) {
838 for (Media media
: element
.getMedia()) {
839 taxonMedia
.add(media
);
846 if (occurrence
instanceof DerivedUnitBase
) {
847 if (((DerivedUnitBase
) occurrence
).getCollection() != null){
848 taxonMedia
.addAll(((DerivedUnitBase
) occurrence
).getCollection().getMedia());
853 if (occurrence
instanceof DnaSample
) {
854 Set
<Sequence
> sequences
= ((DnaSample
) occurrence
).getSequences();
855 for (Sequence sequence
: sequences
) {
856 taxonMedia
.addAll(sequence
.getChromatograms());
863 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
864 // --- TaxonNameDescription
865 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
866 for (Taxon t
: taxa
) {
867 nameDescriptions
.addAll(t
.getName().getDescriptions());
869 for(TaxonNameDescription nameDescription
: nameDescriptions
){
870 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
871 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
872 for (DescriptionElementBase element
: elements
) {
873 for (Media media
: element
.getMedia()) {
874 taxonMedia
.add(media
);
881 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
886 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
889 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
890 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
894 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
897 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
898 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
902 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
905 public int countAllRelationships() {
906 return this.dao
.countAllRelationships();
913 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
916 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
917 return this.dao
.findIdenticalTaxonNames(propertyPath
);
922 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
925 public void deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
) throws ReferencedObjectUndeletableException
{
927 config
= new TaxonDeletionConfigurator();
931 if (! config
.isDeleteTaxonNodes()){
932 if (taxon
.getTaxonNodes().size() > 0){
933 String message
= "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
934 throw new ReferencedObjectUndeletableException(message
);
939 // SynonymRelationShip
940 if (config
.isDeleteSynonymRelations()){
941 boolean removeSynonymNameFromHomotypicalGroup
= false;
942 for (SynonymRelationship synRel
: taxon
.getSynonymRelations()){
943 Synonym synonym
= synRel
.getSynonym();
944 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
945 if (config
.isDeleteSynonymsIfPossible()){
947 boolean newHomotypicGroupIfNeeded
= true;
948 deleteSynonym(synonym
, taxon
, config
.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded
);
950 deleteSynonymRelationships(synonym
, taxon
);
956 if (! config
.isDeleteTaxonRelationships()){
957 if (taxon
.getTaxonRelations().size() > 0){
958 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.";
959 throw new ReferencedObjectUndeletableException(message
);
965 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
967 for (TaxonDescription desc
: descriptions
){
968 if (config
.isDeleteDescriptions()){
969 //TODO use description delete configurator ?
970 //FIXME check if description is ALWAYS deletable
971 descriptionService
.delete(desc
);
973 if (desc
.getDescribedSpecimenOrObservations().size()>0){
974 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
975 " which also describes specimens or abservations";
976 throw new ReferencedObjectUndeletableException(message
);
982 //check references with only reverse mapping
983 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
984 for (CdmBase referencingObject
: referencingObjects
){
985 //IIdentificationKeys (Media, Polytomous, MultiAccess)
986 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
987 String message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
988 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnitBase
.class).getTitleCache());
989 throw new ReferencedObjectUndeletableException(message
);
994 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
995 String message
= "Taxon can't be deleted as it is used in polytomous key node";
996 throw new ReferencedObjectUndeletableException(message
);
1000 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
1001 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1002 throw new ReferencedObjectUndeletableException(message
);
1008 if (config
.isDeleteNameIfPossible()){
1010 nameService
.delete(taxon
.getName(), config
.getNameDeletionConfig());
1011 } catch (ReferencedObjectUndeletableException e
) {
1013 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1020 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1022 @Transactional(readOnly
= false)
1024 public void deleteSynonym(Synonym synonym
, Taxon taxon
, boolean removeNameIfPossible
,boolean newHomotypicGroupIfNeeded
) {
1025 if (synonym
== null){
1028 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1030 //remove synonymRelationship
1031 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1033 taxonSet
.add(taxon
);
1035 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1037 for (Taxon relatedTaxon
: taxonSet
){
1038 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1039 relatedTaxon
.removeSynonym(synonym
, newHomotypicGroupIfNeeded
);
1041 this.saveOrUpdate(synonym
);
1043 //TODO remove name from homotypical group?
1045 //remove synonym (if necessary)
1046 if (synonym
.getSynonymRelations().isEmpty()){
1047 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1048 synonym
.setName(null);
1049 dao
.delete(synonym
);
1051 //remove name if possible (and required)
1052 if (name
!= null && removeNameIfPossible
){
1054 nameService
.delete(name
, new NameDeletionConfigurator());
1055 }catch (DataChangeNoRollbackException ex
){
1056 if (logger
.isDebugEnabled()) {
1057 logger
.debug("Name wasn't deleted as it is referenced");
1066 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1069 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1071 return this.dao
.findIdenticalNamesNew(propertyPath
);
1075 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1078 public String
getPhylumName(TaxonNameBase name
){
1079 return this.dao
.getPhylumName(name
);
1083 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1086 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1087 return dao
.deleteSynonymRelationships(syn
, taxon
);
1091 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1094 public long deleteSynonymRelationships(Synonym syn
) {
1095 return dao
.deleteSynonymRelationships(syn
, null);
1100 * @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)
1103 public List
<SynonymRelationship
> listSynonymRelationships(
1104 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1105 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1106 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1108 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1109 if(numberOfResults
> 0) { // no point checking again
1110 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1116 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1119 public Taxon
findBestMatchingTaxon(String taxonName
) {
1120 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1121 config
.setTaxonNameTitle(taxonName
);
1122 return findBestMatchingTaxon(config
);
1128 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1130 Taxon bestCandidate
= null;
1132 // 1. search for acceptet taxa
1133 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1134 boolean bestCandidateMatchesSecUuid
= false;
1135 boolean bestCandidateIsInClassification
= false;
1136 int countEqualCandidates
= 0;
1137 for(TaxonBase taxonBaseCandidate
: taxonList
){
1138 if(taxonBaseCandidate
instanceof Taxon
){
1139 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1140 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1141 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1143 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1144 bestCandidate
= newCanditate
;
1145 countEqualCandidates
= 1;
1146 bestCandidateMatchesSecUuid
= true;
1150 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1151 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1153 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1154 bestCandidate
= newCanditate
;
1155 countEqualCandidates
= 1;
1156 bestCandidateIsInClassification
= true;
1159 if (bestCandidate
== null){
1160 bestCandidate
= newCanditate
;
1161 countEqualCandidates
= 1;
1165 }else{ //not Taxon.class
1168 countEqualCandidates
++;
1171 if (bestCandidate
!= null){
1172 if(countEqualCandidates
> 1){
1173 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1174 return bestCandidate
;
1176 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1177 return bestCandidate
;
1182 // 2. search for synonyms
1183 if (config
.isIncludeSynonyms()){
1184 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1185 for(TaxonBase taxonBase
: synonymList
){
1186 if(taxonBase
instanceof Synonym
){
1187 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1188 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1189 if(!acceptetdCandidates
.isEmpty()){
1190 bestCandidate
= acceptetdCandidates
.iterator().next();
1191 if(acceptetdCandidates
.size() == 1){
1192 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1193 return bestCandidate
;
1195 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1196 return bestCandidate
;
1198 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1204 } catch (Exception e
){
1206 e
.printStackTrace();
1209 return bestCandidate
;
1212 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1213 UUID configClassificationUuid
= config
.getClassificationUuid();
1214 if (configClassificationUuid
== null){
1217 for (TaxonNode node
: taxon
.getTaxonNodes()){
1218 UUID classUuid
= node
.getClassification().getUuid();
1219 if (configClassificationUuid
.equals(classUuid
)){
1226 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1227 UUID configSecUuid
= config
.getSecUuid();
1228 if (configSecUuid
== null){
1231 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1232 return configSecUuid
.equals(taxonSecUuid
);
1236 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1239 public Synonym
findBestMatchingSynonym(String taxonName
) {
1240 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1241 if(! synonymList
.isEmpty()){
1242 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1243 if(synonymList
.size() == 1){
1244 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1247 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1256 * @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)
1259 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1260 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1262 Synonym synonym
= oldSynonymRelation
.getSynonym();
1263 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1264 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1265 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1266 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1267 //set default relationship type
1268 if (newSynonymRelationshipType
== null){
1269 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1271 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1273 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1274 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1275 boolean isSingleInGroup
= !(hgSize
> 1);
1277 if (! isSingleInGroup
){
1278 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1279 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1280 if (isHomotypicToAccepted
){
1281 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.";
1282 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1283 message
= String
.format(message
, homotypicRelatives
);
1284 throw new HomotypicalGroupChangeException(message
);
1286 if (! moveHomotypicGroup
){
1287 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.";
1288 throw new HomotypicalGroupChangeException(message
);
1291 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1293 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1295 SynonymRelationship result
= null;
1296 //move all synonyms to new taxon
1297 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1298 for (Synonym syn
: homotypicSynonyms
){
1299 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1300 for (SynonymRelationship synRelation
: synRelations
){
1301 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1302 Reference
<?
> newReference
= reference
;
1303 if (newReference
== null && keepReference
){
1304 newReference
= synRelation
.getCitation();
1306 String newRefDetail
= referenceDetail
;
1307 if (newRefDetail
== null && keepReference
){
1308 newRefDetail
= synRelation
.getCitationMicroReference();
1310 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1311 fromTaxon
.removeSynonymRelation(synRelation
, false);
1313 //change homotypic group of synonym if relType is 'homotypic'
1314 // if (newRelTypeIsHomotypic){
1315 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1318 if (synRelation
.equals(oldSynonymRelation
)){
1319 result
= newSynRelation
;
1325 saveOrUpdate(newTaxon
);
1326 //Assert that there is a result
1327 if (result
== null){
1328 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1329 throw new IllegalStateException(message
);
1335 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1338 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1339 return dao
.getUuidAndTitleCacheTaxon();
1343 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1346 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1347 return dao
.getUuidAndTitleCacheSynonym();
1351 * @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)
1354 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1355 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1356 Classification classification
, List
<Language
> languages
,
1357 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1360 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
);
1362 // --- execute search
1363 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1365 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1366 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1368 // --- initialize taxa, thighlight matches ....
1369 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1370 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1371 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1373 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1374 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1378 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1379 Classification classification
,
1380 Integer pageSize
, Integer pageNumber
,
1381 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1383 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1385 // --- execute search
1386 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1388 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1389 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1391 // --- initialize taxa, thighlight matches ....
1392 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1393 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1394 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1396 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1397 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1402 * @param queryString
1403 * @param classification
1405 * @param highlightFragments
1406 * @param directorySelectClass
1409 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1410 boolean highlightFragments
) {
1411 BooleanQuery finalQuery
= new BooleanQuery();
1412 BooleanQuery textQuery
= new BooleanQuery();
1414 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1415 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1417 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1418 luceneSearch
.setSortFields(sortFields
);
1420 // ---- search criteria
1421 luceneSearch
.setCdmTypRestriction(clazz
);
1423 textQuery
.add(taxonBaseQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1424 textQuery
.add(taxonBaseQueryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1426 finalQuery
.add(textQuery
, Occur
.MUST
);
1428 if(classification
!= null){
1429 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1431 luceneSearch
.setQuery(finalQuery
);
1433 if(highlightFragments
){
1434 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1436 return luceneSearch
;
1440 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1441 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1442 * drawback of requiring to do the join an indexing time.
1443 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1445 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1447 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1448 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1451 * @param queryString
1452 * @param classification
1454 * @param highlightFragments
1456 * @throws IOException
1458 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1459 boolean highlightFragments
) throws IOException
{
1462 String queryTermField
;
1463 String toField
= "id"; // TaxonBase.uuid
1465 if(edge
.isBidirectional()){
1466 throw new RuntimeException("Bidirectional joining not supported!");
1469 fromField
= "relatedFrom.id";
1470 queryTermField
= "relatedFrom.titleCache";
1471 } else if(edge
.isInvers()) {
1472 fromField
= "relatedTo.id";
1473 queryTermField
= "relatedTo.titleCache";
1475 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1478 BooleanQuery finalQuery
= new BooleanQuery();
1480 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, TaxonBase
.class);
1481 QueryFactory taxonBaseQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(TaxonBase
.class);
1483 BooleanQuery joinFromQuery
= new BooleanQuery();
1484 joinFromQuery
.add(taxonBaseQueryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1485 joinFromQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1486 Query joinQuery
= taxonBaseQueryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1488 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1489 luceneSearch
.setSortFields(sortFields
);
1491 finalQuery
.add(joinQuery
, Occur
.MUST
);
1493 if(classification
!= null){
1494 finalQuery
.add(taxonBaseQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1496 luceneSearch
.setQuery(finalQuery
);
1498 if(highlightFragments
){
1499 luceneSearch
.setHighlightFields(taxonBaseQueryFactory
.getTextFieldNamesAsArray());
1501 return luceneSearch
;
1508 * @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)
1511 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1512 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1513 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
, boolean highlightFragments
, Integer pageSize
,
1514 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1515 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1517 // convert sets to lists
1518 List
<NamedArea
> namedAreaList
= null;
1519 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1520 if(namedAreas
!= null){
1521 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1522 namedAreaList
.addAll(namedAreas
);
1524 if(distributionStatus
!= null){
1525 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1526 distributionStatusList
.addAll(distributionStatus
);
1529 // set default if parameter is null
1530 if(searchModes
== null){
1531 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1534 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1535 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1538 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1539 Class taxonBaseSubclass
= TaxonBase
.class;
1540 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1541 taxonBaseSubclass
= Taxon
.class;
1542 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1543 taxonBaseSubclass
= Synonym
.class;
1545 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
));
1546 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1548 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1549 luceneSearches
.add(prepareByDescriptionElementFullTextSearch(CommonTaxonName
.class, queryString
, classification
, null, languages
, highlightFragments
));
1550 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1552 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1554 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1555 // which allows doing query time joins
1556 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1557 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1558 queryString
, classification
, languages
, highlightFragments
));
1559 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1562 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1564 if(namedAreas
!= null && namedAreas
.size() > 0){
1565 // - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1566 // - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1567 // add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1568 // which will be put into a FilteredQuersy in the end ?
1571 // 3. how does it work in spatial?
1573 // - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1574 // - http://www.infoq.com/articles/LuceneSpatialSupport
1575 // - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1577 QueryFactory distributionQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Distribution
.class);
1579 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, distributionQueryFactory
);
1580 multiSearch
.setFilter(new QueryWrapperFilter(taxonAreaJoinQuery
));
1583 // --- execute search
1584 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1586 // --- initialize taxa, highlight matches ....
1587 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1590 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1591 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1593 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1594 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1598 * @param namedAreaList at least one area must be in the list
1599 * @param distributionStatusList optional
1601 * @throws IOException
1603 protected Query
createByDistributionJoinQuery(List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1604 QueryFactory queryFactory
) throws IOException
{
1606 String fromField
= "inDescription.taxon.id"; // in DescriptionElementBase index
1607 String toField
= "id"; // id in TaxonBase index
1609 BooleanQuery areaQuery
= new BooleanQuery();
1610 // area field from Distribution
1611 areaQuery
.add(queryFactory
.newEntityIdsQuery("area.id", namedAreaList
), Occur
.MUST
);
1613 // status field from Distribution
1614 if(distributionStatusList
!= null && distributionStatusList
.size() > 0){
1615 areaQuery
.add(queryFactory
.newEntityIdsQuery("status.id", distributionStatusList
), Occur
.MUST
);
1618 logger
.debug("prepareByAreaSearch() query: " + areaQuery
.toString());
1620 Query taxonAreaJoinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, areaQuery
, Distribution
.class);
1622 return taxonAreaJoinQuery
;
1626 * This method has been primarily created for testing the area join query but might
1627 * also be useful in other situations
1629 * @param namedAreaList
1630 * @param distributionStatusList
1631 * @param classification
1632 * @param highlightFragments
1634 * @throws IOException
1636 protected LuceneSearch
prepareByDistributionSearch(
1637 List
<NamedArea
> namedAreaList
, List
<PresenceAbsenceTermBase
<?
>> distributionStatusList
,
1638 Classification classification
) throws IOException
{
1640 BooleanQuery finalQuery
= new BooleanQuery();
1642 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1644 // FIXME is this query factory using the wrong type?
1645 QueryFactory taxonQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(Taxon
.class);
1647 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1648 luceneSearch
.setSortFields(sortFields
);
1651 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, taxonQueryFactory
);
1653 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
1655 if(classification
!= null){
1656 finalQuery
.add(taxonQueryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1659 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
1660 luceneSearch
.setQuery(finalQuery
);
1662 return luceneSearch
;
1668 * @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)
1671 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
1672 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
1673 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
1674 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1677 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
1679 // --- execute search
1680 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1682 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1683 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1685 // --- initialize taxa, highlight matches ....
1686 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1687 @SuppressWarnings("rawtypes")
1688 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1689 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1691 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1692 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1698 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
1699 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
1700 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1702 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
1703 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
);
1705 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneIndexToolProvider
, luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
1707 // --- execute search
1708 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1710 // --- initialize taxa, highlight matches ....
1711 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1713 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1714 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1715 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1717 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1718 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1720 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1721 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1728 * @param queryString
1729 * @param classification
1732 * @param highlightFragments
1733 * @param directorySelectClass
1736 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
1737 String queryString
, Classification classification
, List
<Feature
> features
,
1738 List
<Language
> languages
, boolean highlightFragments
) {
1739 BooleanQuery finalQuery
= new BooleanQuery();
1740 BooleanQuery textQuery
= new BooleanQuery();
1742 LuceneSearch luceneSearch
= new LuceneSearch(luceneIndexToolProvider
, GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
1743 QueryFactory descriptionElementQueryFactory
= luceneIndexToolProvider
.newQueryFactoryFor(DescriptionElementBase
.class);
1745 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
1746 luceneSearch
.setSortFields(sortFields
);
1748 // ---- search criteria
1749 luceneSearch
.setCdmTypRestriction(clazz
);
1750 textQuery
.add(descriptionElementQueryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1754 if(languages
== null || languages
.size() == 0){
1755 nameQuery
= descriptionElementQueryFactory
.newTermQuery("name", queryString
);
1757 nameQuery
= new BooleanQuery();
1758 BooleanQuery languageSubQuery
= new BooleanQuery();
1759 for(Language lang
: languages
){
1760 languageSubQuery
.add(descriptionElementQueryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
1762 ((BooleanQuery
) nameQuery
).add(descriptionElementQueryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
1763 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
1765 textQuery
.add(nameQuery
, Occur
.SHOULD
);
1768 // text field from TextData
1769 textQuery
.add(descriptionElementQueryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
1771 // --- TermBase fields - by representation ----
1772 // state field from CategoricalData
1773 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("states.state", queryString
, languages
), Occur
.SHOULD
);
1775 // state field from CategoricalData
1776 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("states.modifyingText", queryString
, languages
), Occur
.SHOULD
);
1778 // area field from Distribution
1779 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
1781 // status field from Distribution
1782 textQuery
.add(descriptionElementQueryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
1784 finalQuery
.add(textQuery
, Occur
.MUST
);
1785 // --- classification ----
1787 if(classification
!= null){
1788 finalQuery
.add(descriptionElementQueryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
1791 // --- IdentifieableEntity fields - by uuid
1792 if(features
!= null && features
.size() > 0 ){
1793 finalQuery
.add(descriptionElementQueryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
1796 // the description must be associated with a taxon
1797 finalQuery
.add(descriptionElementQueryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
1799 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
1800 luceneSearch
.setQuery(finalQuery
);
1802 if(highlightFragments
){
1803 luceneSearch
.setHighlightFields(descriptionElementQueryFactory
.getTextFieldNamesAsArray());
1805 return luceneSearch
;
1809 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1810 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1811 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1813 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1814 * or {@link MultilanguageTextFieldBridge }
1815 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1816 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1817 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1819 * TODO move to utiliy class !!!!!!!!
1821 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
1823 if(stringBuilder
== null){
1824 stringBuilder
= new StringBuilder();
1826 if(languages
== null || languages
.size() == 0){
1827 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
1829 for(Language lang
: languages
){
1830 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
1833 return stringBuilder
;
1837 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
1838 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
1839 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
1841 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
1844 UUID nameUuid
= taxon
.getName().getUuid();
1845 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
1846 String epithetOfTaxon
= null;
1847 String infragenericEpithetOfTaxon
= null;
1848 String infraspecificEpithetOfTaxon
= null;
1849 if (taxonName
.isSpecies()){
1850 epithetOfTaxon
= taxonName
.getSpecificEpithet();
1851 } else if (taxonName
.isInfraGeneric()){
1852 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
1853 } else if (taxonName
.isInfraSpecific()){
1854 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
1856 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
1857 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1858 List
<String
> taxonNames
= new ArrayList
<String
>();
1860 for (TaxonNode node
: nodes
){
1861 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1862 // List<String> synonymsEpithet = new ArrayList<String>();
1864 if (node
.getClassification().equals(classification
)){
1865 if (!node
.isTopmostNode()){
1866 TaxonNode parent
= node
.getParent();
1867 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
1868 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
1869 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
1870 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
1871 Rank rankOfTaxon
= taxonName
.getRank();
1874 //create inferred synonyms for species, subspecies
1875 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
1877 Synonym inferredEpithet
= null;
1878 Synonym inferredGenus
= null;
1879 Synonym potentialCombination
= null;
1881 List
<String
> propertyPaths
= new ArrayList
<String
>();
1882 propertyPaths
.add("synonym");
1883 propertyPaths
.add("synonym.name");
1884 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
1885 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
1887 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1888 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1890 List
<TaxonRelationship
> taxonRelListParent
= null;
1891 List
<TaxonRelationship
> taxonRelListTaxon
= null;
1892 if (doWithMisappliedNames
){
1893 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
1894 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
1898 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
1901 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
1902 Synonym syn
= synonymRelationOfParent
.getSynonym();
1904 inferredEpithet
= createInferredEpithets(taxon
,
1905 zooHashMap
, taxonName
, epithetOfTaxon
,
1906 infragenericEpithetOfTaxon
,
1907 infraspecificEpithetOfTaxon
,
1908 taxonNames
, parentName
,
1912 inferredSynonyms
.add(inferredEpithet
);
1913 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
1914 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
1917 if (doWithMisappliedNames
){
1919 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
1920 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
1922 inferredEpithet
= createInferredEpithets(taxon
,
1923 zooHashMap
, taxonName
, epithetOfTaxon
,
1924 infragenericEpithetOfTaxon
,
1925 infraspecificEpithetOfTaxon
,
1926 taxonNames
, parentName
,
1929 inferredSynonyms
.add(inferredEpithet
);
1930 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
1931 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
1935 if (!taxonNames
.isEmpty()){
1936 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1937 ZoologicalName name
;
1938 if (!synNotInCDM
.isEmpty()){
1939 inferredSynonymsToBeRemoved
.clear();
1941 for (Synonym syn
:inferredSynonyms
){
1942 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1943 if (!synNotInCDM
.contains(name
.getNameCache())){
1944 inferredSynonymsToBeRemoved
.add(syn
);
1948 // Remove identified Synonyms from inferredSynonyms
1949 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
1950 inferredSynonyms
.remove(synonym
);
1955 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
1958 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
1959 TaxonNameBase synName
;
1960 ZoologicalName inferredSynName
;
1962 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
1963 inferredGenus
= createInferredGenus(taxon
,
1964 zooHashMap
, taxonName
, epithetOfTaxon
,
1965 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
1967 inferredSynonyms
.add(inferredGenus
);
1968 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
1969 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
1974 if (doWithMisappliedNames
){
1976 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
1977 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
1978 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
1980 inferredSynonyms
.add(inferredGenus
);
1981 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
1982 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
1987 if (!taxonNames
.isEmpty()){
1988 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1989 ZoologicalName name
;
1990 if (!synNotInCDM
.isEmpty()){
1991 inferredSynonymsToBeRemoved
.clear();
1993 for (Synonym syn
:inferredSynonyms
){
1994 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1995 if (!synNotInCDM
.contains(name
.getNameCache())){
1996 inferredSynonymsToBeRemoved
.add(syn
);
2000 // Remove identified Synonyms from inferredSynonyms
2001 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2002 inferredSynonyms
.remove(synonym
);
2007 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2009 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2010 ZoologicalName inferredSynName
;
2011 //for all synonyms of the parent...
2012 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2013 TaxonNameBase synName
;
2014 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2015 synName
= synParent
.getName();
2017 HibernateProxyHelper
.deproxy(synParent
);
2019 // Set the sourceReference
2020 sourceReference
= synParent
.getSec();
2022 // Determine the idInSource
2023 String idInSourceParent
= getIdInSource(synParent
);
2025 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2026 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2027 String synParentInfragenericName
= null;
2028 String synParentSpecificEpithet
= null;
2030 if (parentSynZooName
.isInfraGeneric()){
2031 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2033 if (parentSynZooName
.isSpecies()){
2034 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2037 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2038 synonymsGenus.put(synGenusName, idInSource);
2041 //for all synonyms of the taxon
2043 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2045 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2046 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2047 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2049 synParentInfragenericName
,
2050 synParentSpecificEpithet
, syn
, zooHashMap
);
2052 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2053 inferredSynonyms
.add(potentialCombination
);
2054 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2055 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2062 if (doWithMisappliedNames
){
2064 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2066 TaxonNameBase misappliedParentName
;
2068 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2069 misappliedParentName
= misappliedParent
.getName();
2071 HibernateProxyHelper
.deproxy(misappliedParent
);
2073 // Set the sourceReference
2074 sourceReference
= misappliedParent
.getSec();
2076 // Determine the idInSource
2077 String idInSourceParent
= getIdInSource(misappliedParent
);
2079 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2080 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2081 String synParentInfragenericName
= null;
2082 String synParentSpecificEpithet
= null;
2084 if (parentSynZooName
.isInfraGeneric()){
2085 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2087 if (parentSynZooName
.isSpecies()){
2088 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2092 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2093 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2094 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2095 potentialCombination
= createPotentialCombination(
2096 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2098 synParentInfragenericName
,
2099 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2102 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2103 inferredSynonyms
.add(potentialCombination
);
2104 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2105 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2110 if (!taxonNames
.isEmpty()){
2111 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2112 ZoologicalName name
;
2113 if (!synNotInCDM
.isEmpty()){
2114 inferredSynonymsToBeRemoved
.clear();
2115 for (Synonym syn
:inferredSynonyms
){
2117 name
= (ZoologicalName
) syn
.getName();
2118 }catch (ClassCastException e
){
2119 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2121 if (!synNotInCDM
.contains(name
.getNameCache())){
2122 inferredSynonymsToBeRemoved
.add(syn
);
2125 // Remove identified Synonyms from inferredSynonyms
2126 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2127 inferredSynonyms
.remove(synonym
);
2133 logger
.info("The synonymrelationship type is not defined.");
2134 return inferredSynonyms
;
2141 return inferredSynonyms
;
2144 private Synonym
createPotentialCombination(String idInSourceParent
,
2145 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2146 String synParentInfragenericName
, String synParentSpecificEpithet
,
2147 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2148 Synonym potentialCombination
;
2149 Reference sourceReference
;
2150 ZoologicalName inferredSynName
;
2151 HibernateProxyHelper
.deproxy(syn
);
2153 // Set sourceReference
2154 sourceReference
= syn
.getSec();
2155 if (sourceReference
== null){
2156 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2158 if (!parentSynZooName
.getTaxa().isEmpty()){
2159 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2161 sourceReference
= taxon
.getSec();
2164 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2166 String synTaxonInfraSpecificName
= null;
2168 if (parentSynZooName
.isSpecies()){
2169 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2172 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2173 synonymsEpithet.add(epithetName);
2176 //create potential combinations...
2177 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2179 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2180 if (zooSynName
.isSpecies()){
2181 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2182 if (parentSynZooName
.isInfraGeneric()){
2183 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2186 if (zooSynName
.isInfraSpecific()){
2187 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2188 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2190 if (parentSynZooName
.isInfraGeneric()){
2191 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2195 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2197 // Set the sourceReference
2198 potentialCombination
.setSec(sourceReference
);
2201 // Determine the idInSource
2202 String idInSourceSyn
= getIdInSource(syn
);
2204 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2205 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2206 inferredSynName
.addSource(originalSource
);
2207 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2208 potentialCombination
.addSource(originalSource
);
2211 inferredSynName
.generateTitle();
2213 return potentialCombination
;
2216 private Synonym
createInferredGenus(Taxon taxon
,
2217 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2218 String epithetOfTaxon
, String genusOfTaxon
,
2219 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2222 Synonym inferredGenus
;
2223 TaxonNameBase synName
;
2224 ZoologicalName inferredSynName
;
2225 synName
=syn
.getName();
2226 HibernateProxyHelper
.deproxy(syn
);
2228 // Determine the idInSource
2229 String idInSourceSyn
= getIdInSource(syn
);
2230 String idInSourceTaxon
= getIdInSource(taxon
);
2231 // Determine the sourceReference
2232 Reference sourceReference
= syn
.getSec();
2234 //logger.warn(sourceReference.getTitleCache());
2236 synName
= syn
.getName();
2237 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2238 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2239 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2240 synonymsEpithet.add(synSpeciesEpithetName);
2243 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2244 //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...
2247 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2248 if (zooParentName
.isInfraGeneric()){
2249 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2252 if (taxonName
.isSpecies()){
2253 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2255 if (taxonName
.isInfraSpecific()){
2256 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2257 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2261 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2263 // Set the sourceReference
2264 inferredGenus
.setSec(sourceReference
);
2266 // Add the original source
2267 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2268 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2269 inferredGenus
.addSource(originalSource
);
2271 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2272 inferredSynName
.addSource(originalSource
);
2273 originalSource
= null;
2276 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2277 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2278 inferredGenus
.addSource(originalSource
);
2280 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2281 inferredSynName
.addSource(originalSource
);
2282 originalSource
= null;
2285 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2287 inferredSynName
.generateTitle();
2290 return inferredGenus
;
2293 private Synonym
createInferredEpithets(Taxon taxon
,
2294 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2295 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2296 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2297 TaxonNameBase parentName
, TaxonBase syn
) {
2299 Synonym inferredEpithet
;
2300 TaxonNameBase
<?
,?
> synName
;
2301 ZoologicalName inferredSynName
;
2302 HibernateProxyHelper
.deproxy(syn
);
2304 // Determine the idInSource
2305 String idInSourceSyn
= getIdInSource(syn
);
2306 String idInSourceTaxon
= getIdInSource(taxon
);
2307 // Determine the sourceReference
2308 Reference
<?
> sourceReference
= syn
.getSec();
2310 if (sourceReference
== null){
2311 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2312 sourceReference
= taxon
.getSec();
2315 synName
= syn
.getName();
2316 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2317 String synGenusName
= zooSynName
.getGenusOrUninomial();
2318 String synInfraGenericEpithet
= null;
2319 String synSpecificEpithet
= null;
2321 if (zooSynName
.getInfraGenericEpithet() != null){
2322 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2325 if (zooSynName
.isInfraSpecific()){
2326 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2329 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2330 synonymsGenus.put(synGenusName, idInSource);
2333 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2335 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2336 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2337 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2339 inferredSynName
.setGenusOrUninomial(synGenusName
);
2341 if (parentName
.isInfraGeneric()){
2342 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2344 if (taxonName
.isSpecies()){
2345 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2346 }else if (taxonName
.isInfraSpecific()){
2347 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2348 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2351 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2353 // Set the sourceReference
2354 inferredEpithet
.setSec(sourceReference
);
2356 /* Add the original source
2357 if (idInSource != null) {
2358 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2361 Reference citation = getCitation(syn);
2362 if (citation != null) {
2363 originalSource.setCitation(citation);
2364 inferredEpithet.addSource(originalSource);
2367 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2370 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2372 inferredEpithet
.addSource(originalSource
);
2374 originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2376 inferredSynName
.addSource(originalSource
);
2380 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2382 inferredSynName
.generateTitle();
2383 return inferredEpithet
;
2387 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2388 * Very likely only useful for createInferredSynonyms().
2393 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2394 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2395 if (taxonName
== null) {
2396 taxonName
= zooHashMap
.get(uuid
);
2402 * Returns the idInSource for a given Synonym.
2405 private String
getIdInSource(TaxonBase taxonBase
) {
2406 String idInSource
= null;
2407 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2408 if (sources
.size() == 1) {
2409 IdentifiableSource source
= sources
.iterator().next();
2410 if (source
!= null) {
2411 idInSource
= source
.getIdInSource();
2413 } else if (sources
.size() > 1) {
2416 for (IdentifiableSource source
: sources
) {
2417 idInSource
+= source
.getIdInSource();
2418 if (count
< sources
.size()) {
2423 } else if (sources
.size() == 0){
2424 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2433 * Returns the citation for a given Synonym.
2436 private Reference
getCitation(Synonym syn
) {
2437 Reference citation
= null;
2438 Set
<IdentifiableSource
> sources
= syn
.getSources();
2439 if (sources
.size() == 1) {
2440 IdentifiableSource source
= sources
.iterator().next();
2441 if (source
!= null) {
2442 citation
= source
.getCitation();
2444 } else if (sources
.size() > 1) {
2445 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2452 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2453 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2455 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2456 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2457 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2459 return inferredSynonyms
;
2463 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2466 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2468 // TODO quickly implemented, create according dao !!!!
2469 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2470 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2471 List
<Classification
> list
= new ArrayList
<Classification
>();
2473 if (taxonBase
== null) {
2477 taxonBase
= load(taxonBase
.getUuid());
2479 if (taxonBase
instanceof Taxon
) {
2480 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2482 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2483 nodes
.addAll(taxon
.getTaxonNodes());
2486 for (TaxonNode node
: nodes
) {
2487 classifications
.add(node
.getClassification());
2489 list
.addAll(classifications
);