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
.IndexSearcher
;
29 import org
.apache
.lucene
.search
.Query
;
30 import org
.apache
.lucene
.search
.QueryWrapperFilter
;
31 import org
.apache
.lucene
.search
.SortField
;
32 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
33 import org
.springframework
.stereotype
.Service
;
34 import org
.springframework
.transaction
.annotation
.Transactional
;
36 import eu
.etaxonomy
.cdm
.api
.service
.config
.IFindTaxaAndNamesConfigurator
;
37 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
38 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
39 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
40 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
41 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
42 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
43 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
44 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
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
;
153 private static IndexSearcher taxonRelationshipSearcher
;
158 public TaxonServiceImpl(){
159 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
163 * FIXME Candidate for harmonization
164 * rename searchByName ?
167 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
168 return dao
.getTaxaByName(name
, sec
);
172 * FIXME Candidate for harmonization
173 * list(Synonym.class, ...)
175 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
178 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
179 return dao
.getAllSynonyms(limit
, start
);
183 * FIXME Candidate for harmonization
184 * list(Taxon.class, ...)
186 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
189 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
190 return dao
.getAllTaxa(limit
, start
);
194 * FIXME Candidate for harmonization
195 * merge with getRootTaxa(Reference sec, ..., ...)
197 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
200 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
201 if (cdmFetch
== null){
202 cdmFetch
= CdmFetch
.NO_FETCH();
204 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
209 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
212 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
213 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
217 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
220 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
221 return dao
.getAllRelationships(limit
, start
);
225 * FIXME Candidate for harmonization
226 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
230 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
232 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
233 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
234 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
235 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
236 return taxonRelTypeVocabulary
;
243 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
246 @Transactional(readOnly
= false)
247 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
249 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
250 synonymName
.removeTaxonBase(synonym
);
251 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
252 taxonName
.removeTaxonBase(acceptedTaxon
);
254 synonym
.setName(taxonName
);
255 acceptedTaxon
.setName(synonymName
);
257 // the accepted taxon needs a new uuid because the concept has changed
258 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
259 //acceptedTaxon.setUuid(UUID.randomUUID());
264 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
266 //TODO correct delete handling still needs to be implemented / checked
268 @Transactional(readOnly
= false)
269 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
271 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
272 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
273 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
275 //check synonym is not homotypic
276 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
277 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
278 throw new HomotypicalGroupChangeException(message
);
281 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
283 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
284 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
286 for (Synonym heteroSynonym
: heteroSynonyms
){
287 if (synonym
.equals(heteroSynonym
)){
288 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
290 //move synonyms in same homotypic group to new accepted taxon
291 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
295 //synonym.getName().removeTaxonBase(synonym);
296 //TODO correct delete handling still needs to be implemented / checked
298 // deleteSynonym(synonym, taxon, false);
301 this.delete(synonym
);
303 } catch (Exception e
) {
304 logger
.info("Can't delete old synonym from database");
308 return newAcceptedTaxon
;
313 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
315 // Get name from synonym
316 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
318 // remove synonym from taxon
319 toTaxon
.removeSynonym(synonym
);
321 // Create a taxon with synonym name
322 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
324 // Add taxon relation
325 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
327 // since we are swapping names, we have to detach the name from the synonym completely.
328 // Otherwise the synonym will still be in the list of typified names.
329 synonym
.getName().removeTaxonBase(synonym
);
336 * @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)
338 @Transactional(readOnly
= false)
340 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
341 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
343 TaxonNameBase synonymName
= synonym
.getName();
344 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
348 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
349 newHomotypicalGroup
.addTypifiedName(synonymName
);
351 //remove existing basionym relationships
352 synonymName
.removeBasionyms();
354 //add basionym relationship
355 if (setBasionymRelationIfApplicable
){
356 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
357 for (TaxonNameBase basionym
: basionyms
){
358 synonymName
.addBasionym(basionym
);
362 //set synonym relationship correctly
363 // SynonymRelationship relToTaxon = null;
364 boolean relToTargetTaxonExists
= false;
365 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
366 for (SynonymRelationship rel
: existingRelations
){
367 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
368 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
369 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
370 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
371 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
372 rel
.setType(newRelationType
);
373 //TODO handle citation and microCitation
376 relToTargetTaxonExists
= true;
378 if (removeFromOtherTaxa
){
379 acceptedTaxon
.removeSynonym(synonym
, false);
385 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
386 Taxon acceptedTaxon
= targetTaxon
;
387 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
388 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
389 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
390 //TODO handle citation and microCitation
391 Reference citation
= null;
392 String microCitation
= null;
393 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
400 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
403 @Transactional(readOnly
= false)
404 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
406 clazz
= TaxonBase
.class;
408 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
413 protected void setDao(ITaxonDao dao
) {
418 * @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)
421 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
,
422 String uninomial
, String infragenericEpithet
, String specificEpithet
,
423 String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
424 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
426 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
427 if(numberOfResults
> 0) { // no point checking again
428 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
431 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
436 * @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)
439 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
440 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
442 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
443 if(numberOfResults
> 0) { // no point checking again
444 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
451 * @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)
454 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
455 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
457 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
458 if(numberOfResults
> 0) { // no point checking again
459 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
465 * @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)
468 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
469 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
471 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
472 if(numberOfResults
> 0) { // no point checking again
473 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
475 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
479 * @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)
482 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
483 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
485 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
486 if(numberOfResults
> 0) { // no point checking again
487 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
493 * @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)
496 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
497 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
499 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
500 if(numberOfResults
> 0) { // no point checking again
501 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
503 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
508 * @param includeRelationships
512 * @param propertyPaths
513 * @return an List which is not specifically ordered
516 public Set
<Taxon
> listRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Integer maxDepth
,
517 Integer limit
, Integer start
, List
<String
> propertyPaths
) {
519 Set
<Taxon
> relatedTaxa
= collectRelatedTaxa(taxon
, includeRelationships
, new HashSet
<Taxon
>(), maxDepth
);
520 relatedTaxa
.remove(taxon
);
521 beanInitializer
.initializeAll(relatedTaxa
, propertyPaths
);
527 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
528 * <code>taxon</code> supplied as parameter.
531 * @param includeRelationships
533 * @param maxDepth can be <code>null</code> for infinite depth
536 private Set
<Taxon
> collectRelatedTaxa(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, Set
<Taxon
> taxa
, Integer maxDepth
) {
542 if(maxDepth
!= null) {
545 if(logger
.isDebugEnabled()){
546 logger
.debug("collecting related taxa for " + taxon
+ " with maxDepth=" + maxDepth
);
548 List
<TaxonRelationship
> taxonRelationships
= dao
.getTaxonRelationships(taxon
, null, null, null, null, null, null);
549 for (TaxonRelationship taxRel
: taxonRelationships
) {
552 if (taxRel
.getToTaxon() == null || taxRel
.getFromTaxon() == null || taxRel
.getType() == null) {
555 // filter by includeRelationships
556 for (TaxonRelationshipEdge relationshipEdgeFilter
: includeRelationships
) {
557 if ( relationshipEdgeFilter
.getTaxonRelationshipType().equals(taxRel
.getType()) ) {
558 if (relationshipEdgeFilter
.getDirections().contains(Direction
.relatedTo
) && !taxa
.contains(taxRel
.getToTaxon())) {
559 if(logger
.isDebugEnabled()){
560 logger
.debug(maxDepth
+ ": " + taxon
.getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxRel
.getToTaxon().getTitleCache());
562 taxa
.add(taxRel
.getToTaxon());
563 if(maxDepth
== null || maxDepth
> 0) {
564 taxa
.addAll(collectRelatedTaxa(taxRel
.getToTaxon(), includeRelationships
, taxa
, maxDepth
));
567 if(relationshipEdgeFilter
.getDirections().contains(Direction
.relatedFrom
) && !taxa
.contains(taxRel
.getFromTaxon())) {
568 taxa
.add(taxRel
.getFromTaxon());
569 if(logger
.isDebugEnabled()){
570 logger
.debug(maxDepth
+ ": " +taxRel
.getFromTaxon().getTitleCache() + " --[" + taxRel
.getType().getLabel() + "]--> " + taxon
.getTitleCache() );
572 if(maxDepth
== null || maxDepth
> 0) {
573 taxa
.addAll(collectRelatedTaxa(taxRel
.getFromTaxon(), includeRelationships
, taxa
, maxDepth
));
583 * @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)
586 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
587 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
589 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
590 if(numberOfResults
> 0) { // no point checking again
591 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
594 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
598 * @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)
601 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
602 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
604 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
605 if(numberOfResults
> 0) { // no point checking again
606 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
609 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
613 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
616 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
617 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
618 return t
.getHomotypicSynonymsByHomotypicGroup();
622 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
625 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
626 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
627 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
628 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
629 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
630 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
632 return heterotypicSynonymyGroups
;
636 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator
){
638 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
639 // Class<? extends TaxonBase> clazz = null;
640 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
641 // clazz = TaxonBase.class;
642 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
643 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
644 // } else if(configurator.isDoTaxa()) {
645 // clazz = Taxon.class;
646 // //propertyPath = configurator.getTaxonPropertyPath();
647 // } else if (configurator.isDoSynonyms()) {
648 // clazz = Synonym.class;
649 // //propertyPath = configurator.getSynonymPropertyPath();
653 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
658 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
661 public Pager
<IdentifiableEntity
> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator
) {
663 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
664 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
665 List
<TaxonBase
> taxa
= null;
668 long numberTaxaResults
= 0L;
671 List
<String
> propertyPath
= new ArrayList
<String
>();
672 if(configurator
.getTaxonPropertyPath() != null){
673 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
677 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
678 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
680 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
681 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
682 configurator
.getNamedAreas());
685 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
686 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
687 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
688 configurator
.getMatchMode(), configurator
.getNamedAreas(),
689 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
693 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
696 results
.addAll(taxa
);
699 numberOfResults
+= numberTaxaResults
;
701 // Names without taxa
702 if (configurator
.isDoNamesWithoutTaxa()) {
703 int numberNameResults
= 0;
705 List
<?
extends TaxonNameBase
<?
,?
>> names
=
706 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
707 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
708 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
709 if (names
.size() > 0) {
710 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
711 if (taxonName
.getTaxonBases().size() == 0) {
712 results
.add(taxonName
);
716 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
717 numberOfResults
+= numberNameResults
;
721 // Taxa from common names
723 if (configurator
.isDoTaxaByCommonNames()) {
724 taxa
= new ArrayList
<TaxonBase
>();
725 numberTaxaResults
= 0;
726 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
727 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
729 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
730 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
731 for( Object
[] entry
: commonNameResults
) {
732 taxa
.add((TaxonBase
) entry
[0]);
736 results
.addAll(taxa
);
738 numberOfResults
+= numberTaxaResults
;
742 return new DefaultPagerImpl
<IdentifiableEntity
>
743 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
746 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
747 return dao
.getUuidAndTitleCache();
751 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
754 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
755 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
756 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
757 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
758 for (TaxonDescription taxDesc
: descriptions
){
759 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
760 for (DescriptionElementBase descElem
: elements
){
761 for(Media media
: descElem
.getMedia()){
763 //find the best matching representation
764 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
773 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
776 public List
<Media
> listTaxonDescriptionMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
, boolean limitToGalleries
, List
<String
> propertyPath
){
777 return listMedia(taxon
, includeRelationships
, limitToGalleries
, true, false, false, propertyPath
);
782 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
785 public List
<Media
> listMedia(Taxon taxon
, Set
<TaxonRelationshipEdge
> includeRelationships
,
786 Boolean limitToGalleries
, Boolean includeTaxonDescriptions
, Boolean includeOccurrences
,
787 Boolean includeTaxonNameDescriptions
, List
<String
> propertyPath
) {
789 Set
<Taxon
> taxa
= new HashSet
<Taxon
>();
790 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
792 if (limitToGalleries
== null) {
793 limitToGalleries
= false;
796 // --- resolve related taxa
797 if (includeRelationships
!= null) {
798 taxa
= listRelatedTaxa(taxon
, includeRelationships
, null, null, null, null);
801 taxa
.add((Taxon
) dao
.load(taxon
.getUuid()));
803 if(includeTaxonDescriptions
!= null && includeTaxonDescriptions
){
804 List
<TaxonDescription
> taxonDescriptions
= new ArrayList
<TaxonDescription
>();
805 // --- TaxonDescriptions
806 for (Taxon t
: taxa
) {
807 taxonDescriptions
.addAll(descriptionService
.listTaxonDescriptions(t
, null, null, null, null, propertyPath
));
809 for (TaxonDescription taxonDescription
: taxonDescriptions
) {
810 if (!limitToGalleries
|| taxonDescription
.isImageGallery()) {
811 for (DescriptionElementBase element
: taxonDescription
.getElements()) {
812 for (Media media
: element
.getMedia()) {
813 taxonMedia
.add(media
);
820 if(includeOccurrences
!= null && includeOccurrences
) {
821 Set
<SpecimenOrObservationBase
> specimensOrObservations
= new HashSet
<SpecimenOrObservationBase
>();
823 for (Taxon t
: taxa
) {
824 specimensOrObservations
.addAll(occurrenceDao
.listByAssociatedTaxon(null, t
, null, null, null, null));
826 for (SpecimenOrObservationBase occurrence
: specimensOrObservations
) {
828 taxonMedia
.addAll(occurrence
.getMedia());
830 // SpecimenDescriptions
831 Set
<SpecimenDescription
> specimenDescriptions
= occurrence
.getSpecimenDescriptions();
832 for (DescriptionBase specimenDescription
: specimenDescriptions
) {
833 if (!limitToGalleries
|| specimenDescription
.isImageGallery()) {
834 Set
<DescriptionElementBase
> elements
= specimenDescription
.getElements();
835 for (DescriptionElementBase element
: elements
) {
836 for (Media media
: element
.getMedia()) {
837 taxonMedia
.add(media
);
844 if (occurrence
instanceof DerivedUnitBase
) {
845 if (((DerivedUnitBase
) occurrence
).getCollection() != null){
846 taxonMedia
.addAll(((DerivedUnitBase
) occurrence
).getCollection().getMedia());
851 if (occurrence
instanceof DnaSample
) {
852 Set
<Sequence
> sequences
= ((DnaSample
) occurrence
).getSequences();
853 for (Sequence sequence
: sequences
) {
854 taxonMedia
.addAll(sequence
.getChromatograms());
861 if(includeTaxonNameDescriptions
!= null && includeTaxonNameDescriptions
) {
862 // --- TaxonNameDescription
863 Set
<TaxonNameDescription
> nameDescriptions
= new HashSet
<TaxonNameDescription
>();
864 for (Taxon t
: taxa
) {
865 nameDescriptions
.addAll(t
.getName().getDescriptions());
867 for(TaxonNameDescription nameDescription
: nameDescriptions
){
868 if (!limitToGalleries
|| nameDescription
.isImageGallery()) {
869 Set
<DescriptionElementBase
> elements
= nameDescription
.getElements();
870 for (DescriptionElementBase element
: elements
) {
871 for (Media media
: element
.getMedia()) {
872 taxonMedia
.add(media
);
879 beanInitializer
.initializeAll(taxonMedia
, propertyPath
);
884 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
887 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
888 return this.dao
.listByIds(listOfIDs
, null, null, null, null);
892 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
895 public TaxonBase
findTaxonByUuid(UUID uuid
, List
<String
> propertyPaths
){
896 return this.dao
.findByUuid(uuid
, null ,propertyPaths
);
900 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
903 public int countAllRelationships() {
904 return this.dao
.countAllRelationships();
911 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
914 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
915 return this.dao
.findIdenticalTaxonNames(propertyPath
);
920 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
923 public void deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
) throws ReferencedObjectUndeletableException
{
925 config
= new TaxonDeletionConfigurator();
929 if (! config
.isDeleteTaxonNodes()){
930 if (taxon
.getTaxonNodes().size() > 0){
931 String message
= "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
932 throw new ReferencedObjectUndeletableException(message
);
937 // SynonymRelationShip
938 if (config
.isDeleteSynonymRelations()){
939 boolean removeSynonymNameFromHomotypicalGroup
= false;
940 for (SynonymRelationship synRel
: taxon
.getSynonymRelations()){
941 Synonym synonym
= synRel
.getSynonym();
942 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
943 if (config
.isDeleteSynonymsIfPossible()){
945 boolean newHomotypicGroupIfNeeded
= true;
946 deleteSynonym(synonym
, taxon
, config
.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded
);
948 deleteSynonymRelationships(synonym
, taxon
);
954 if (! config
.isDeleteTaxonRelationships()){
955 if (taxon
.getTaxonRelations().size() > 0){
956 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.";
957 throw new ReferencedObjectUndeletableException(message
);
963 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
965 for (TaxonDescription desc
: descriptions
){
966 if (config
.isDeleteDescriptions()){
967 //TODO use description delete configurator ?
968 //FIXME check if description is ALWAYS deletable
969 descriptionService
.delete(desc
);
971 if (desc
.getDescribedSpecimenOrObservations().size()>0){
972 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
973 " which also describes specimens or abservations";
974 throw new ReferencedObjectUndeletableException(message
);
980 //check references with only reverse mapping
981 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
982 for (CdmBase referencingObject
: referencingObjects
){
983 //IIdentificationKeys (Media, Polytomous, MultiAccess)
984 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
985 String message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
986 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnitBase
.class).getTitleCache());
987 throw new ReferencedObjectUndeletableException(message
);
992 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
993 String message
= "Taxon can't be deleted as it is used in polytomous key node";
994 throw new ReferencedObjectUndeletableException(message
);
998 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
999 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1000 throw new ReferencedObjectUndeletableException(message
);
1006 if (config
.isDeleteNameIfPossible()){
1008 nameService
.delete(taxon
.getName(), config
.getNameDeletionConfig());
1009 } catch (ReferencedObjectUndeletableException e
) {
1011 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
1018 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1020 @Transactional(readOnly
= false)
1022 public void deleteSynonym(Synonym synonym
, Taxon taxon
, boolean removeNameIfPossible
,boolean newHomotypicGroupIfNeeded
) {
1023 if (synonym
== null){
1026 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
1028 //remove synonymRelationship
1029 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
1031 taxonSet
.add(taxon
);
1033 taxonSet
.addAll(synonym
.getAcceptedTaxa());
1035 for (Taxon relatedTaxon
: taxonSet
){
1036 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1037 relatedTaxon
.removeSynonym(synonym
, newHomotypicGroupIfNeeded
);
1039 this.saveOrUpdate(synonym
);
1041 //TODO remove name from homotypical group?
1043 //remove synonym (if necessary)
1044 if (synonym
.getSynonymRelations().isEmpty()){
1045 TaxonNameBase
<?
,?
> name
= synonym
.getName();
1046 synonym
.setName(null);
1047 dao
.delete(synonym
);
1049 //remove name if possible (and required)
1050 if (name
!= null && removeNameIfPossible
){
1052 nameService
.delete(name
, new NameDeletionConfigurator());
1053 }catch (DataChangeNoRollbackException ex
){
1054 if (logger
.isDebugEnabled()) {
1055 logger
.debug("Name wasn't deleted as it is referenced");
1064 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1067 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
1069 return this.dao
.findIdenticalNamesNew(propertyPath
);
1073 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1076 public String
getPhylumName(TaxonNameBase name
){
1077 return this.dao
.getPhylumName(name
);
1081 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1084 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
1085 return dao
.deleteSynonymRelationships(syn
, taxon
);
1089 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1092 public long deleteSynonymRelationships(Synonym syn
) {
1093 return dao
.deleteSynonymRelationships(syn
, null);
1098 * @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)
1101 public List
<SynonymRelationship
> listSynonymRelationships(
1102 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
1103 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
1104 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
1106 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
1107 if(numberOfResults
> 0) { // no point checking again
1108 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
1114 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1117 public Taxon
findBestMatchingTaxon(String taxonName
) {
1118 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
1119 config
.setTaxonNameTitle(taxonName
);
1120 return findBestMatchingTaxon(config
);
1126 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
1128 Taxon bestCandidate
= null;
1130 // 1. search for acceptet taxa
1131 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1132 boolean bestCandidateMatchesSecUuid
= false;
1133 boolean bestCandidateIsInClassification
= false;
1134 int countEqualCandidates
= 0;
1135 for(TaxonBase taxonBaseCandidate
: taxonList
){
1136 if(taxonBaseCandidate
instanceof Taxon
){
1137 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
1138 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
1139 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
1141 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
1142 bestCandidate
= newCanditate
;
1143 countEqualCandidates
= 1;
1144 bestCandidateMatchesSecUuid
= true;
1148 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
1149 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
1151 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
1152 bestCandidate
= newCanditate
;
1153 countEqualCandidates
= 1;
1154 bestCandidateIsInClassification
= true;
1157 if (bestCandidate
== null){
1158 bestCandidate
= newCanditate
;
1159 countEqualCandidates
= 1;
1163 }else{ //not Taxon.class
1166 countEqualCandidates
++;
1169 if (bestCandidate
!= null){
1170 if(countEqualCandidates
> 1){
1171 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
1172 return bestCandidate
;
1174 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
1175 return bestCandidate
;
1180 // 2. search for synonyms
1181 if (config
.isIncludeSynonyms()){
1182 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
1183 for(TaxonBase taxonBase
: synonymList
){
1184 if(taxonBase
instanceof Synonym
){
1185 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
1186 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
1187 if(!acceptetdCandidates
.isEmpty()){
1188 bestCandidate
= acceptetdCandidates
.iterator().next();
1189 if(acceptetdCandidates
.size() == 1){
1190 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
1191 return bestCandidate
;
1193 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
1194 return bestCandidate
;
1196 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1202 } catch (Exception e
){
1204 e
.printStackTrace();
1207 return bestCandidate
;
1210 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
1211 UUID configClassificationUuid
= config
.getClassificationUuid();
1212 if (configClassificationUuid
== null){
1215 for (TaxonNode node
: taxon
.getTaxonNodes()){
1216 UUID classUuid
= node
.getClassification().getUuid();
1217 if (configClassificationUuid
.equals(classUuid
)){
1224 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
1225 UUID configSecUuid
= config
.getSecUuid();
1226 if (configSecUuid
== null){
1229 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
1230 return configSecUuid
.equals(taxonSecUuid
);
1234 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1237 public Synonym
findBestMatchingSynonym(String taxonName
) {
1238 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
1239 if(! synonymList
.isEmpty()){
1240 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
1241 if(synonymList
.size() == 1){
1242 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
1245 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
1254 * @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)
1257 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
1258 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
1260 Synonym synonym
= oldSynonymRelation
.getSynonym();
1261 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
1262 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1263 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
1264 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
1265 //set default relationship type
1266 if (newSynonymRelationshipType
== null){
1267 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1269 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1271 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1272 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1273 boolean isSingleInGroup
= !(hgSize
> 1);
1275 if (! isSingleInGroup
){
1276 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1277 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1278 if (isHomotypicToAccepted
){
1279 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.";
1280 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1281 message
= String
.format(message
, homotypicRelatives
);
1282 throw new HomotypicalGroupChangeException(message
);
1284 if (! moveHomotypicGroup
){
1285 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.";
1286 throw new HomotypicalGroupChangeException(message
);
1289 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1291 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1293 SynonymRelationship result
= null;
1294 //move all synonyms to new taxon
1295 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1296 for (Synonym syn
: homotypicSynonyms
){
1297 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1298 for (SynonymRelationship synRelation
: synRelations
){
1299 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1300 Reference
<?
> newReference
= reference
;
1301 if (newReference
== null && keepReference
){
1302 newReference
= synRelation
.getCitation();
1304 String newRefDetail
= referenceDetail
;
1305 if (newRefDetail
== null && keepReference
){
1306 newRefDetail
= synRelation
.getCitationMicroReference();
1308 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1309 fromTaxon
.removeSynonymRelation(synRelation
, false);
1311 //change homotypic group of synonym if relType is 'homotypic'
1312 // if (newRelTypeIsHomotypic){
1313 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1316 if (synRelation
.equals(oldSynonymRelation
)){
1317 result
= newSynRelation
;
1323 saveOrUpdate(newTaxon
);
1324 //Assert that there is a result
1325 if (result
== null){
1326 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1327 throw new IllegalStateException(message
);
1333 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1336 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1337 return dao
.getUuidAndTitleCacheTaxon();
1341 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1344 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1345 return dao
.getUuidAndTitleCacheSynonym();
1349 * @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)
1352 public Pager
<SearchResult
<TaxonBase
>> findByFullText(
1353 Class
<?
extends TaxonBase
> clazz
, String queryString
,
1354 Classification classification
, List
<Language
> languages
,
1355 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1358 LuceneSearch luceneSearch
= prepareFindByFullTextSearch(clazz
, queryString
, classification
, languages
, highlightFragments
);
1360 // --- execute search
1361 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1363 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1364 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1366 // --- initialize taxa, thighlight matches ....
1367 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1368 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1369 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1371 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1372 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1376 public Pager
<SearchResult
<TaxonBase
>> findByDistribution(List
<NamedArea
> areaFilter
, List
<PresenceAbsenceTermBase
<?
>> statusFilter
,
1377 Classification classification
,
1378 Integer pageSize
, Integer pageNumber
,
1379 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws IOException
, ParseException
{
1381 LuceneSearch luceneSearch
= prepareByDistributionSearch(areaFilter
, statusFilter
, classification
);
1383 // --- execute search
1384 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1386 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1387 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1389 // --- initialize taxa, thighlight matches ....
1390 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1391 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1392 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1394 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1395 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1400 * @param queryString
1401 * @param classification
1403 * @param highlightFragments
1404 * @param directorySelectClass
1407 protected LuceneSearch
prepareFindByFullTextSearch(Class
<?
extends CdmBase
> clazz
, String queryString
, Classification classification
, List
<Language
> languages
,
1408 boolean highlightFragments
) {
1409 BooleanQuery finalQuery
= new BooleanQuery();
1410 BooleanQuery textQuery
= new BooleanQuery();
1412 LuceneSearch luceneSearch
= new LuceneSearch(getSession(), GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, TaxonBase
.class);
1413 QueryFactory queryFactory
= new QueryFactory(luceneSearch
);
1415 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1416 luceneSearch
.setSortFields(sortFields
);
1418 // ---- search criteria
1419 luceneSearch
.setClazz(clazz
);
1421 textQuery
.add(queryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1422 textQuery
.add(queryFactory
.newDefinedTermQuery("name.rank", queryString
, languages
), Occur
.SHOULD
);
1424 finalQuery
.add(textQuery
, Occur
.MUST
);
1426 if(classification
!= null){
1427 finalQuery
.add(queryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1429 luceneSearch
.setQuery(finalQuery
);
1431 if(highlightFragments
){
1432 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
1434 return luceneSearch
;
1438 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1439 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1440 * drawback of requiring to do the join an indexing time.
1441 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1443 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1445 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --> Taxon.id </li>
1446 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --> Taxon.id </li>
1449 * @param queryString
1450 * @param classification
1452 * @param highlightFragments
1454 * @throws IOException
1456 protected LuceneSearch
prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge
, String queryString
, Classification classification
, List
<Language
> languages
,
1457 boolean highlightFragments
) throws IOException
{
1460 String queryTermField
;
1461 String toField
= "id"; // TaxonBase.uuid
1463 if(edge
.isBidirectional()){
1464 throw new RuntimeException("Bidirectional joining not supported!");
1467 fromField
= "relatedFrom.id";
1468 queryTermField
= "relatedFrom.titleCache";
1469 } else if(edge
.isInvers()) {
1470 fromField
= "relatedTo.id";
1471 queryTermField
= "relatedTo.titleCache";
1473 throw new RuntimeException("Invalid direction: " + edge
.getDirections());
1476 BooleanQuery finalQuery
= new BooleanQuery();
1478 LuceneSearch luceneSearch
= new LuceneSearch(getSession(), TaxonBase
.class);
1479 QueryFactory queryFactory
= new QueryFactory(luceneSearch
);
1481 BooleanQuery joinFromQuery
= new BooleanQuery();
1482 joinFromQuery
.add(queryFactory
.newTermQuery(queryTermField
, queryString
), Occur
.MUST
);
1483 joinFromQuery
.add(queryFactory
.newEntityIdQuery("type.id", edge
.getTaxonRelationshipType()), Occur
.MUST
);
1484 Query joinQuery
= queryFactory
.newJoinQuery(fromField
, toField
, joinFromQuery
, TaxonRelationship
.class);
1486 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1487 luceneSearch
.setSortFields(sortFields
);
1489 finalQuery
.add(joinQuery
, Occur
.MUST
);
1491 if(classification
!= null){
1492 finalQuery
.add(queryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1494 luceneSearch
.setQuery(finalQuery
);
1496 if(highlightFragments
){
1497 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
1499 return luceneSearch
;
1506 * @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)
1509 public Pager
<SearchResult
<TaxonBase
>> findTaxaAndNamesByFullText(
1510 EnumSet
<TaxaAndNamesSearchMode
> searchModes
, String queryString
, Classification classification
,
1511 Set
<NamedArea
> namedAreas
, Set
<PresenceAbsenceTermBase
<?
>> distributionStatus
, List
<Language
> languages
, boolean highlightFragments
, Integer pageSize
,
1512 Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
)
1513 throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1515 // convert sets to lists
1516 List
<NamedArea
> namedAreaList
= null;
1517 List
<PresenceAbsenceTermBase
<?
>>distributionStatusList
= null;
1518 if(namedAreas
!= null){
1519 namedAreaList
= new ArrayList
<NamedArea
>(namedAreas
.size());
1520 namedAreaList
.addAll(namedAreas
);
1522 if(distributionStatus
!= null){
1523 distributionStatusList
= new ArrayList
<PresenceAbsenceTermBase
<?
>>(distributionStatus
.size());
1524 distributionStatusList
.addAll(distributionStatus
);
1527 // set default if parameter is null
1528 if(searchModes
== null){
1529 searchModes
= EnumSet
.of(TaxaAndNamesSearchMode
.doTaxa
);
1532 List
<LuceneSearch
> luceneSearches
= new ArrayList
<LuceneSearch
>();
1533 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1536 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) || searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1537 Class taxonBaseSubclass
= TaxonBase
.class;
1538 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && !searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)){
1539 taxonBaseSubclass
= Taxon
.class;
1540 } else if (!searchModes
.contains(TaxaAndNamesSearchMode
.doTaxa
) && searchModes
.contains(TaxaAndNamesSearchMode
.doSynonyms
)) {
1541 taxonBaseSubclass
= Synonym
.class;
1543 luceneSearches
.add(prepareFindByFullTextSearch(taxonBaseSubclass
, queryString
, classification
, languages
, highlightFragments
));
1544 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1546 if(searchModes
.contains(TaxaAndNamesSearchMode
.doTaxaByCommonNames
)) {
1547 luceneSearches
.add(prepareByDescriptionElementFullTextSearch(CommonTaxonName
.class, queryString
, classification
, null, languages
, highlightFragments
));
1548 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1550 if(searchModes
.contains(TaxaAndNamesSearchMode
.doMisappliedNames
)) {
1552 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
1553 // which allows doing query time joins
1554 luceneSearches
.add(prepareFindByTaxonRelationFullTextSearch(
1555 new TaxonRelationshipEdge(TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), Direction
.relatedTo
),
1556 queryString
, classification
, languages
, highlightFragments
));
1557 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1560 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneSearches
.toArray(new LuceneSearch
[luceneSearches
.size()]));
1562 if(namedAreas
!= null && namedAreas
.size() > 0){
1563 // - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1564 // - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1565 // add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1566 // which will be put into a FilteredQuersy in the end ?
1569 // 3. how does it work in spacial?
1571 // - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1572 // - http://www.infoq.com/articles/LuceneSpatialSupport
1573 // - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1576 // TODO can't we use a static QueryFactory field?
1577 QueryFactory queryFactory
= new QueryFactory(luceneSearches
.get(0));
1579 Query taxonAreaJoinQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, queryFactory
);
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(getSession(), GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, Taxon
.class);
1643 QueryFactory queryFactory
= new QueryFactory(luceneSearch
);
1645 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("titleCache__sort", SortField
.STRING
, false)};
1646 luceneSearch
.setSortFields(sortFields
);
1649 Query byAreaQuery
= createByDistributionJoinQuery(namedAreaList
, distributionStatusList
, queryFactory
);
1651 finalQuery
.add(byAreaQuery
, Occur
.MUST
);
1653 if(classification
!= null){
1654 finalQuery
.add(queryFactory
.newEntityIdQuery("taxonNodes.classification.id", classification
), Occur
.MUST
);
1657 logger
.info("prepareByAreaSearch() query: " + finalQuery
.toString());
1658 luceneSearch
.setQuery(finalQuery
);
1660 return luceneSearch
;
1666 * @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)
1669 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(
1670 Class
<?
extends DescriptionElementBase
> clazz
, String queryString
,
1671 Classification classification
, List
<Feature
> features
, List
<Language
> languages
,
1672 boolean highlightFragments
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1675 LuceneSearch luceneSearch
= prepareByDescriptionElementFullTextSearch(clazz
, queryString
, classification
, features
, languages
, highlightFragments
);
1677 // --- execute search
1678 TopGroupsWithMaxScore topDocsResultSet
= luceneSearch
.executeSearch(pageSize
, pageNumber
);
1680 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1681 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1683 // --- initialize taxa, highlight matches ....
1684 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(luceneSearch
, luceneSearch
.getQuery());
1685 @SuppressWarnings("rawtypes")
1686 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1687 topDocsResultSet
, luceneSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1689 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1690 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1696 public Pager
<SearchResult
<TaxonBase
>> findByEverythingFullText(String queryString
,
1697 Classification classification
, List
<Language
> languages
, boolean highlightFragments
,
1698 Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
, LuceneMultiSearchException
{
1700 LuceneSearch luceneSearchByDescriptionElement
= prepareByDescriptionElementFullTextSearch(null, queryString
, classification
, null, languages
, highlightFragments
);
1701 LuceneSearch luceneSearchByTaxonBase
= prepareFindByFullTextSearch(null, queryString
, classification
, languages
, highlightFragments
);
1703 LuceneMultiSearch multiSearch
= new LuceneMultiSearch(luceneSearchByDescriptionElement
, luceneSearchByTaxonBase
);
1705 // --- execute search
1706 TopGroupsWithMaxScore topDocsResultSet
= multiSearch
.executeSearch(pageSize
, pageNumber
);
1708 // --- initialize taxa, highlight matches ....
1709 ISearchResultBuilder searchResultBuilder
= new SearchResultBuilder(multiSearch
, multiSearch
.getQuery());
1711 Map
<CdmBaseType
, String
> idFieldMap
= new HashMap
<CdmBaseType
, String
>();
1712 idFieldMap
.put(CdmBaseType
.TAXON
, "id");
1713 idFieldMap
.put(CdmBaseType
.DESCRIPTION_ELEMENT
, "inDescription.taxon.id");
1715 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSet(
1716 topDocsResultSet
, multiSearch
.getHighlightFields(), dao
, idFieldMap
, propertyPaths
);
1718 int totalHits
= topDocsResultSet
!= null ? topDocsResultSet
.topGroups
.totalGroupCount
: 0;
1719 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, totalHits
, pageSize
, searchResults
);
1726 * @param queryString
1727 * @param classification
1730 * @param highlightFragments
1731 * @param directorySelectClass
1734 protected LuceneSearch
prepareByDescriptionElementFullTextSearch(Class
<?
extends CdmBase
> clazz
,
1735 String queryString
, Classification classification
, List
<Feature
> features
,
1736 List
<Language
> languages
, boolean highlightFragments
) {
1737 BooleanQuery finalQuery
= new BooleanQuery();
1738 BooleanQuery textQuery
= new BooleanQuery();
1740 LuceneSearch luceneSearch
= new LuceneSearch(getSession(), GroupByTaxonClassBridge
.GROUPBY_TAXON_FIELD
, DescriptionElementBase
.class);
1741 QueryFactory queryFactory
= new QueryFactory(luceneSearch
);
1743 SortField
[] sortFields
= new SortField
[]{SortField
.FIELD_SCORE
, new SortField("inDescription.taxon.titleCache__sort", SortField
.STRING
, false)};
1744 luceneSearch
.setSortFields(sortFields
);
1746 // ---- search criteria
1747 luceneSearch
.setClazz(clazz
);
1748 textQuery
.add(queryFactory
.newTermQuery("titleCache", queryString
), Occur
.SHOULD
);
1752 if(languages
== null || languages
.size() == 0){
1753 nameQuery
= queryFactory
.newTermQuery("name", queryString
);
1755 nameQuery
= new BooleanQuery();
1756 BooleanQuery languageSubQuery
= new BooleanQuery();
1757 for(Language lang
: languages
){
1758 languageSubQuery
.add(queryFactory
.newTermQuery("language.uuid", lang
.getUuid().toString(), false), Occur
.SHOULD
);
1760 ((BooleanQuery
) nameQuery
).add(queryFactory
.newTermQuery("name", queryString
), Occur
.MUST
);
1761 ((BooleanQuery
) nameQuery
).add(languageSubQuery
, Occur
.MUST
);
1763 textQuery
.add(nameQuery
, Occur
.SHOULD
);
1766 // text field from TextData
1767 textQuery
.add(queryFactory
.newMultilanguageTextQuery("text", queryString
, languages
), Occur
.SHOULD
);
1769 // --- TermBase fields - by representation ----
1770 // state field from CategoricalData
1771 textQuery
.add(queryFactory
.newDefinedTermQuery("states.state", queryString
, languages
), Occur
.SHOULD
);
1773 // state field from CategoricalData
1774 textQuery
.add(queryFactory
.newDefinedTermQuery("states.modifyingText", queryString
, languages
), Occur
.SHOULD
);
1776 // area field from Distribution
1777 textQuery
.add(queryFactory
.newDefinedTermQuery("area", queryString
, languages
), Occur
.SHOULD
);
1779 // status field from Distribution
1780 textQuery
.add(queryFactory
.newDefinedTermQuery("status", queryString
, languages
), Occur
.SHOULD
);
1782 finalQuery
.add(textQuery
, Occur
.MUST
);
1783 // --- classification ----
1785 if(classification
!= null){
1786 finalQuery
.add(queryFactory
.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification
), Occur
.MUST
);
1789 // --- IdentifieableEntity fields - by uuid
1790 if(features
!= null && features
.size() > 0 ){
1791 finalQuery
.add(queryFactory
.newEntityUuidsQuery("feature.uuid", features
), Occur
.MUST
);
1794 // the description must be associated with a taxon
1795 finalQuery
.add(queryFactory
.newIsNotNullQuery("inDescription.taxon.id"), Occur
.MUST
);
1797 logger
.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery
.toString());
1798 luceneSearch
.setQuery(finalQuery
);
1800 if(highlightFragments
){
1801 luceneSearch
.setHighlightFields(queryFactory
.getTextFieldNamesAsArray());
1803 return luceneSearch
;
1807 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1808 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1809 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1811 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1812 * or {@link MultilanguageTextFieldBridge }
1813 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1814 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1815 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1817 * TODO move to utiliy class !!!!!!!!
1819 private StringBuilder
appendLocalizedFieldQuery(String name
, List
<Language
> languages
, StringBuilder stringBuilder
) {
1821 if(stringBuilder
== null){
1822 stringBuilder
= new StringBuilder();
1824 if(languages
== null || languages
.size() == 0){
1825 stringBuilder
.append(name
+ ".ALL:(%1$s) ");
1827 for(Language lang
: languages
){
1828 stringBuilder
.append(name
+ "." + lang
.getUuid().toString() + ":(%1$s) ");
1831 return stringBuilder
;
1835 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
, boolean doWithMisappliedNames
){
1836 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
1837 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
1839 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
1842 UUID nameUuid
= taxon
.getName().getUuid();
1843 ZoologicalName taxonName
= getZoologicalName(nameUuid
, zooHashMap
);
1844 String epithetOfTaxon
= null;
1845 String infragenericEpithetOfTaxon
= null;
1846 String infraspecificEpithetOfTaxon
= null;
1847 if (taxonName
.isSpecies()){
1848 epithetOfTaxon
= taxonName
.getSpecificEpithet();
1849 } else if (taxonName
.isInfraGeneric()){
1850 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
1851 } else if (taxonName
.isInfraSpecific()){
1852 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
1854 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
1855 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1856 List
<String
> taxonNames
= new ArrayList
<String
>();
1858 for (TaxonNode node
: nodes
){
1859 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1860 // List<String> synonymsEpithet = new ArrayList<String>();
1862 if (node
.getClassification().equals(classification
)){
1863 if (!node
.isTopmostNode()){
1864 TaxonNode parent
= node
.getParent();
1865 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
1866 TaxonNameBase
<?
,?
> parentName
= parent
.getTaxon().getName();
1867 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
1868 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
1869 Rank rankOfTaxon
= taxonName
.getRank();
1872 //create inferred synonyms for species, subspecies
1873 if ((parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())) ){
1875 Synonym inferredEpithet
= null;
1876 Synonym inferredGenus
= null;
1877 Synonym potentialCombination
= null;
1879 List
<String
> propertyPaths
= new ArrayList
<String
>();
1880 propertyPaths
.add("synonym");
1881 propertyPaths
.add("synonym.name");
1882 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
1883 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
1885 List
<SynonymRelationship
> synonymRelationshipsOfParent
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1886 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1888 List
<TaxonRelationship
> taxonRelListParent
= null;
1889 List
<TaxonRelationship
> taxonRelListTaxon
= null;
1890 if (doWithMisappliedNames
){
1891 taxonRelListParent
= dao
.getTaxonRelationships(parentTaxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
1892 taxonRelListTaxon
= dao
.getTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, orderHints
, propertyPaths
, Direction
.relatedTo
);
1896 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
1899 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
1900 Synonym syn
= synonymRelationOfParent
.getSynonym();
1902 inferredEpithet
= createInferredEpithets(taxon
,
1903 zooHashMap
, taxonName
, epithetOfTaxon
,
1904 infragenericEpithetOfTaxon
,
1905 infraspecificEpithetOfTaxon
,
1906 taxonNames
, parentName
,
1910 inferredSynonyms
.add(inferredEpithet
);
1911 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
1912 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
1915 if (doWithMisappliedNames
){
1917 for (TaxonRelationship taxonRelationship
: taxonRelListParent
){
1918 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
1920 inferredEpithet
= createInferredEpithets(taxon
,
1921 zooHashMap
, taxonName
, epithetOfTaxon
,
1922 infragenericEpithetOfTaxon
,
1923 infraspecificEpithetOfTaxon
,
1924 taxonNames
, parentName
,
1927 inferredSynonyms
.add(inferredEpithet
);
1928 zooHashMap
.put(inferredEpithet
.getName().getUuid(), (ZoologicalName
)inferredEpithet
.getName());
1929 taxonNames
.add(((ZoologicalName
)inferredEpithet
.getName()).getNameCache());
1933 if (!taxonNames
.isEmpty()){
1934 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1935 ZoologicalName name
;
1936 if (!synNotInCDM
.isEmpty()){
1937 inferredSynonymsToBeRemoved
.clear();
1939 for (Synonym syn
:inferredSynonyms
){
1940 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1941 if (!synNotInCDM
.contains(name
.getNameCache())){
1942 inferredSynonymsToBeRemoved
.add(syn
);
1946 // Remove identified Synonyms from inferredSynonyms
1947 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
1948 inferredSynonyms
.remove(synonym
);
1953 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
1956 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
1957 TaxonNameBase synName
;
1958 ZoologicalName inferredSynName
;
1960 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
1961 inferredGenus
= createInferredGenus(taxon
,
1962 zooHashMap
, taxonName
, epithetOfTaxon
,
1963 genusOfTaxon
, taxonNames
, zooParentName
, syn
);
1965 inferredSynonyms
.add(inferredGenus
);
1966 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
1967 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
1972 if (doWithMisappliedNames
){
1974 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
1975 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
1976 inferredGenus
= createInferredGenus(taxon
, zooHashMap
, taxonName
, infraspecificEpithetOfTaxon
, genusOfTaxon
, taxonNames
, zooParentName
, misappliedName
);
1978 inferredSynonyms
.add(inferredGenus
);
1979 zooHashMap
.put(inferredGenus
.getName().getUuid(), (ZoologicalName
)inferredGenus
.getName());
1980 taxonNames
.add(( (ZoologicalName
)inferredGenus
.getName()).getNameCache());
1985 if (!taxonNames
.isEmpty()){
1986 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1987 ZoologicalName name
;
1988 if (!synNotInCDM
.isEmpty()){
1989 inferredSynonymsToBeRemoved
.clear();
1991 for (Synonym syn
:inferredSynonyms
){
1992 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1993 if (!synNotInCDM
.contains(name
.getNameCache())){
1994 inferredSynonymsToBeRemoved
.add(syn
);
1998 // Remove identified Synonyms from inferredSynonyms
1999 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2000 inferredSynonyms
.remove(synonym
);
2005 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
2007 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
2008 ZoologicalName inferredSynName
;
2009 //for all synonyms of the parent...
2010 for (SynonymRelationship synonymRelationOfParent
:synonymRelationshipsOfParent
){
2011 TaxonNameBase synName
;
2012 Synonym synParent
= synonymRelationOfParent
.getSynonym();
2013 synName
= synParent
.getName();
2015 HibernateProxyHelper
.deproxy(synParent
);
2017 // Set the sourceReference
2018 sourceReference
= synParent
.getSec();
2020 // Determine the idInSource
2021 String idInSourceParent
= getIdInSource(synParent
);
2023 ZoologicalName parentSynZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2024 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2025 String synParentInfragenericName
= null;
2026 String synParentSpecificEpithet
= null;
2028 if (parentSynZooName
.isInfraGeneric()){
2029 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2031 if (parentSynZooName
.isSpecies()){
2032 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2035 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2036 synonymsGenus.put(synGenusName, idInSource);
2039 //for all synonyms of the taxon
2041 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
2043 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
2044 ZoologicalName zooSynName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2045 potentialCombination
= createPotentialCombination(idInSourceParent
, parentSynZooName
, zooSynName
,
2047 synParentInfragenericName
,
2048 synParentSpecificEpithet
, syn
, zooHashMap
);
2050 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2051 inferredSynonyms
.add(potentialCombination
);
2052 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2053 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2060 if (doWithMisappliedNames
){
2062 for (TaxonRelationship parentRelationship
: taxonRelListParent
){
2064 TaxonNameBase misappliedParentName
;
2066 Taxon misappliedParent
= parentRelationship
.getFromTaxon();
2067 misappliedParentName
= misappliedParent
.getName();
2069 HibernateProxyHelper
.deproxy(misappliedParent
);
2071 // Set the sourceReference
2072 sourceReference
= misappliedParent
.getSec();
2074 // Determine the idInSource
2075 String idInSourceParent
= getIdInSource(misappliedParent
);
2077 ZoologicalName parentSynZooName
= getZoologicalName(misappliedParentName
.getUuid(), zooHashMap
);
2078 String synParentGenus
= parentSynZooName
.getGenusOrUninomial();
2079 String synParentInfragenericName
= null;
2080 String synParentSpecificEpithet
= null;
2082 if (parentSynZooName
.isInfraGeneric()){
2083 synParentInfragenericName
= parentSynZooName
.getInfraGenericEpithet();
2085 if (parentSynZooName
.isSpecies()){
2086 synParentSpecificEpithet
= parentSynZooName
.getSpecificEpithet();
2090 for (TaxonRelationship taxonRelationship
: taxonRelListTaxon
){
2091 Taxon misappliedName
= taxonRelationship
.getFromTaxon();
2092 ZoologicalName zooMisappliedName
= getZoologicalName(misappliedName
.getName().getUuid(), zooHashMap
);
2093 potentialCombination
= createPotentialCombination(
2094 idInSourceParent
, parentSynZooName
, zooMisappliedName
,
2096 synParentInfragenericName
,
2097 synParentSpecificEpithet
, misappliedName
, zooHashMap
);
2100 taxon
.addSynonym(potentialCombination
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF());
2101 inferredSynonyms
.add(potentialCombination
);
2102 zooHashMap
.put(potentialCombination
.getName().getUuid(), (ZoologicalName
)potentialCombination
.getName());
2103 taxonNames
.add(( (ZoologicalName
)potentialCombination
.getName()).getNameCache());
2108 if (!taxonNames
.isEmpty()){
2109 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
2110 ZoologicalName name
;
2111 if (!synNotInCDM
.isEmpty()){
2112 inferredSynonymsToBeRemoved
.clear();
2113 for (Synonym syn
:inferredSynonyms
){
2115 name
= (ZoologicalName
) syn
.getName();
2116 }catch (ClassCastException e
){
2117 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
2119 if (!synNotInCDM
.contains(name
.getNameCache())){
2120 inferredSynonymsToBeRemoved
.add(syn
);
2123 // Remove identified Synonyms from inferredSynonyms
2124 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
2125 inferredSynonyms
.remove(synonym
);
2131 logger
.info("The synonymrelationship type is not defined.");
2132 return inferredSynonyms
;
2139 return inferredSynonyms
;
2142 private Synonym
createPotentialCombination(String idInSourceParent
,
2143 ZoologicalName parentSynZooName
, ZoologicalName zooSynName
, String synParentGenus
,
2144 String synParentInfragenericName
, String synParentSpecificEpithet
,
2145 TaxonBase syn
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2146 Synonym potentialCombination
;
2147 Reference sourceReference
;
2148 ZoologicalName inferredSynName
;
2149 HibernateProxyHelper
.deproxy(syn
);
2151 // Set sourceReference
2152 sourceReference
= syn
.getSec();
2153 if (sourceReference
== null){
2154 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2156 if (!parentSynZooName
.getTaxa().isEmpty()){
2157 TaxonBase taxon
= parentSynZooName
.getTaxa().iterator().next();
2159 sourceReference
= taxon
.getSec();
2162 String synTaxonSpecificEpithet
= zooSynName
.getSpecificEpithet();
2164 String synTaxonInfraSpecificName
= null;
2166 if (parentSynZooName
.isSpecies()){
2167 synTaxonInfraSpecificName
= zooSynName
.getInfraSpecificEpithet();
2170 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2171 synonymsEpithet.add(epithetName);
2174 //create potential combinations...
2175 inferredSynName
= ZoologicalName
.NewInstance(syn
.getName().getRank());
2177 inferredSynName
.setGenusOrUninomial(synParentGenus
);
2178 if (zooSynName
.isSpecies()){
2179 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
2180 if (parentSynZooName
.isInfraGeneric()){
2181 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2184 if (zooSynName
.isInfraSpecific()){
2185 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
2186 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
2188 if (parentSynZooName
.isInfraGeneric()){
2189 inferredSynName
.setInfraGenericEpithet(synParentInfragenericName
);
2193 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
2195 // Set the sourceReference
2196 potentialCombination
.setSec(sourceReference
);
2199 // Determine the idInSource
2200 String idInSourceSyn
= getIdInSource(syn
);
2202 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
2203 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2204 inferredSynName
.addSource(originalSource
);
2205 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
2206 potentialCombination
.addSource(originalSource
);
2209 inferredSynName
.generateTitle();
2211 return potentialCombination
;
2214 private Synonym
createInferredGenus(Taxon taxon
,
2215 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2216 String epithetOfTaxon
, String genusOfTaxon
,
2217 List
<String
> taxonNames
, ZoologicalName zooParentName
,
2220 Synonym inferredGenus
;
2221 TaxonNameBase synName
;
2222 ZoologicalName inferredSynName
;
2223 synName
=syn
.getName();
2224 HibernateProxyHelper
.deproxy(syn
);
2226 // Determine the idInSource
2227 String idInSourceSyn
= getIdInSource(syn
);
2228 String idInSourceTaxon
= getIdInSource(taxon
);
2229 // Determine the sourceReference
2230 Reference sourceReference
= syn
.getSec();
2232 //logger.warn(sourceReference.getTitleCache());
2234 synName
= syn
.getName();
2235 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2236 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
2237 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2238 synonymsEpithet.add(synSpeciesEpithetName);
2241 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2242 //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...
2245 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
2246 if (zooParentName
.isInfraGeneric()){
2247 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
2250 if (taxonName
.isSpecies()){
2251 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
2253 if (taxonName
.isInfraSpecific()){
2254 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2255 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
2259 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
2261 // Set the sourceReference
2262 inferredGenus
.setSec(sourceReference
);
2264 // Add the original source
2265 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
2266 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2267 inferredGenus
.addSource(originalSource
);
2269 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2270 inferredSynName
.addSource(originalSource
);
2271 originalSource
= null;
2274 logger
.error("There is an idInSource missing: " + idInSourceSyn
+ " of Synonym or " + idInSourceTaxon
+ " of Taxon");
2275 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2276 inferredGenus
.addSource(originalSource
);
2278 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
2279 inferredSynName
.addSource(originalSource
);
2280 originalSource
= null;
2283 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
2285 inferredSynName
.generateTitle();
2288 return inferredGenus
;
2291 private Synonym
createInferredEpithets(Taxon taxon
,
2292 HashMap
<UUID
, ZoologicalName
> zooHashMap
, ZoologicalName taxonName
,
2293 String epithetOfTaxon
, String infragenericEpithetOfTaxon
,
2294 String infraspecificEpithetOfTaxon
, List
<String
> taxonNames
,
2295 TaxonNameBase parentName
, TaxonBase syn
) {
2297 Synonym inferredEpithet
;
2298 TaxonNameBase
<?
,?
> synName
;
2299 ZoologicalName inferredSynName
;
2300 HibernateProxyHelper
.deproxy(syn
);
2302 // Determine the idInSource
2303 String idInSourceSyn
= getIdInSource(syn
);
2304 String idInSourceTaxon
= getIdInSource(taxon
);
2305 // Determine the sourceReference
2306 Reference
<?
> sourceReference
= syn
.getSec();
2308 if (sourceReference
== null){
2309 logger
.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon
.getSec());
2310 sourceReference
= taxon
.getSec();
2313 synName
= syn
.getName();
2314 ZoologicalName zooSynName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
2315 String synGenusName
= zooSynName
.getGenusOrUninomial();
2316 String synInfraGenericEpithet
= null;
2317 String synSpecificEpithet
= null;
2319 if (zooSynName
.getInfraGenericEpithet() != null){
2320 synInfraGenericEpithet
= zooSynName
.getInfraGenericEpithet();
2323 if (zooSynName
.isInfraSpecific()){
2324 synSpecificEpithet
= zooSynName
.getSpecificEpithet();
2327 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2328 synonymsGenus.put(synGenusName, idInSource);
2331 inferredSynName
= ZoologicalName
.NewInstance(taxon
.getName().getRank());
2333 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2334 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
2335 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
2337 inferredSynName
.setGenusOrUninomial(synGenusName
);
2339 if (parentName
.isInfraGeneric()){
2340 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
2342 if (taxonName
.isSpecies()){
2343 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
2344 }else if (taxonName
.isInfraSpecific()){
2345 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
2346 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
2349 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
2351 // Set the sourceReference
2352 inferredEpithet
.setSec(sourceReference
);
2354 /* Add the original source
2355 if (idInSource != null) {
2356 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2359 Reference citation = getCitation(syn);
2360 if (citation != null) {
2361 originalSource.setCitation(citation);
2362 inferredEpithet.addSource(originalSource);
2365 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
2368 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2370 inferredEpithet
.addSource(originalSource
);
2372 originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
2374 inferredSynName
.addSource(originalSource
);
2378 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
2380 inferredSynName
.generateTitle();
2381 return inferredEpithet
;
2385 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2386 * Very likely only useful for createInferredSynonyms().
2391 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
2392 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
2393 if (taxonName
== null) {
2394 taxonName
= zooHashMap
.get(uuid
);
2400 * Returns the idInSource for a given Synonym.
2403 private String
getIdInSource(TaxonBase taxonBase
) {
2404 String idInSource
= null;
2405 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
2406 if (sources
.size() == 1) {
2407 IdentifiableSource source
= sources
.iterator().next();
2408 if (source
!= null) {
2409 idInSource
= source
.getIdInSource();
2411 } else if (sources
.size() > 1) {
2414 for (IdentifiableSource source
: sources
) {
2415 idInSource
+= source
.getIdInSource();
2416 if (count
< sources
.size()) {
2421 } else if (sources
.size() == 0){
2422 logger
.warn("No idInSource for TaxonBase " + taxonBase
.getUuid() + " - " + taxonBase
.getTitleCache());
2431 * Returns the citation for a given Synonym.
2434 private Reference
getCitation(Synonym syn
) {
2435 Reference citation
= null;
2436 Set
<IdentifiableSource
> sources
= syn
.getSources();
2437 if (sources
.size() == 1) {
2438 IdentifiableSource source
= sources
.iterator().next();
2439 if (source
!= null) {
2440 citation
= source
.getCitation();
2442 } else if (sources
.size() > 1) {
2443 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
2450 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
, boolean doWithMisappliedNames
){
2451 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
2453 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF(), doWithMisappliedNames
));
2454 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF(), doWithMisappliedNames
));
2455 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames
));
2457 return inferredSynonyms
;
2461 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2464 public List
<Classification
> listClassifications(TaxonBase taxonBase
, Integer limit
, Integer start
, List
<String
> propertyPaths
) {
2466 // TODO quickly implemented, create according dao !!!!
2467 Set
<TaxonNode
> nodes
= new HashSet
<TaxonNode
>();
2468 Set
<Classification
> classifications
= new HashSet
<Classification
>();
2469 List
<Classification
> list
= new ArrayList
<Classification
>();
2471 if (taxonBase
== null) {
2475 taxonBase
= load(taxonBase
.getUuid());
2477 if (taxonBase
instanceof Taxon
) {
2478 nodes
.addAll(((Taxon
)taxonBase
).getTaxonNodes());
2480 for (Taxon taxon
: ((Synonym
)taxonBase
).getAcceptedTaxa() ) {
2481 nodes
.addAll(taxon
.getTaxonNodes());
2484 for (TaxonNode node
: nodes
) {
2485 classifications
.add(node
.getClassification());
2487 list
.addAll(classifications
);