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
.HashMap
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
19 import java
.util
.UUID
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.apache
.lucene
.index
.CorruptIndexException
;
23 import org
.apache
.lucene
.queryParser
.ParseException
;
24 import org
.apache
.lucene
.search
.TopDocs
;
25 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
26 import org
.springframework
.stereotype
.Service
;
27 import org
.springframework
.transaction
.annotation
.Propagation
;
28 import org
.springframework
.transaction
.annotation
.Transactional
;
30 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
31 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
32 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
33 import eu
.etaxonomy
.cdm
.api
.service
.config
.TaxonDeletionConfigurator
;
34 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
35 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
36 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
37 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
38 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
39 import eu
.etaxonomy
.cdm
.api
.service
.search
.ISearchResultBuilder
;
40 import eu
.etaxonomy
.cdm
.api
.service
.search
.SearchResult
;
41 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
42 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
43 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
44 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
45 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
46 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
47 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
48 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
49 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
50 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
51 import eu
.etaxonomy
.cdm
.model
.description
.IIdentificationKey
;
52 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
53 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
54 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
55 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
56 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
57 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
58 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
59 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
60 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
61 import eu
.etaxonomy
.cdm
.model
.name
.ZoologicalName
;
62 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
63 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
67 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
68 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
69 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
70 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
71 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
72 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
73 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmGenericDao
;
74 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
75 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
76 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
77 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
78 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
79 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
80 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
81 import eu
.etaxonomy
.cdm
.search
.LuceneSearch
;
82 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
86 * @author a.kohlbecker
91 @Transactional(propagation
= Propagation
.SUPPORTS
, readOnly
= true)
92 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
93 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
95 public static final String POTENTIAL_COMBINATION_NAMESPACE
= "Potential combination";
97 public static final String INFERRED_EPITHET_NAMESPACE
= "Inferred epithet";
99 public static final String INFERRED_GENUS_NAMESPACE
= "Inferred genus";
103 private ITaxonNameDao nameDao
;
106 private ISearchResultBuilder searchResultBuilder
;
109 private INameService nameService
;
112 private ICdmGenericDao genericDao
;
115 private IDescriptionService descriptionService
;
118 private IOrderedTermVocabularyDao orderedVocabularyDao
;
123 public TaxonServiceImpl(){
124 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
128 * FIXME Candidate for harmonization
129 * rename searchByName ?
131 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
132 return dao
.getTaxaByName(name
, sec
);
136 * FIXME Candidate for harmonization
137 * list(Synonym.class, ...)
139 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
141 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
142 return dao
.getAllSynonyms(limit
, start
);
146 * FIXME Candidate for harmonization
147 * list(Taxon.class, ...)
149 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
151 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
152 return dao
.getAllTaxa(limit
, start
);
156 * FIXME Candidate for harmonization
157 * merge with getRootTaxa(Reference sec, ..., ...)
159 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
161 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
162 if (cdmFetch
== null){
163 cdmFetch
= CdmFetch
.NO_FETCH();
165 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
170 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
172 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
173 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
177 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
179 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
180 return dao
.getAllRelationships(limit
, start
);
184 * FIXME Candidate for harmonization
185 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
188 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
190 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
191 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
192 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
193 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
194 return taxonRelTypeVocabulary
;
201 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
203 @Transactional(readOnly
= false)
204 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
206 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
207 synonymName
.removeTaxonBase(synonym
);
208 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
209 taxonName
.removeTaxonBase(acceptedTaxon
);
211 synonym
.setName(taxonName
);
212 acceptedTaxon
.setName(synonymName
);
214 // the accepted taxon needs a new uuid because the concept has changed
215 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
216 //acceptedTaxon.setUuid(UUID.randomUUID());
221 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
223 //TODO correct delete handling still needs to be implemented / checked
225 @Transactional(readOnly
= false)
226 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws HomotypicalGroupChangeException
{
228 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
229 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
230 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
232 //check synonym is not homotypic
233 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
234 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
235 throw new HomotypicalGroupChangeException(message
);
238 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
240 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
241 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
243 for (Synonym heteroSynonym
: heteroSynonyms
){
244 if (synonym
.equals(heteroSynonym
)){
245 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
247 //move synonyms in same homotypic group to new accepted taxon
248 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
252 //synonym.getName().removeTaxonBase(synonym);
253 //TODO correct delete handling still needs to be implemented / checked
255 // deleteSynonym(synonym, taxon, false);
258 this.delete(synonym
);
260 } catch (Exception e
) {
261 logger
.info("Can't delete old synonym from database");
265 return newAcceptedTaxon
;
269 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
271 // Get name from synonym
272 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
274 // remove synonym from taxon
275 toTaxon
.removeSynonym(synonym
);
277 // Create a taxon with synonym name
278 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
280 // Add taxon relation
281 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
283 // since we are swapping names, we have to detach the name from the synonym completely.
284 // Otherwise the synonym will still be in the list of typified names.
285 synonym
.getName().removeTaxonBase(synonym
);
292 * @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)
294 @Transactional(readOnly
= false)
296 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
297 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
299 TaxonNameBase synonymName
= synonym
.getName();
300 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
304 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
305 newHomotypicalGroup
.addTypifiedName(synonymName
);
307 //remove existing basionym relationships
308 synonymName
.removeBasionyms();
310 //add basionym relationship
311 if (setBasionymRelationIfApplicable
){
312 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
313 for (TaxonNameBase basionym
: basionyms
){
314 synonymName
.addBasionym(basionym
);
318 //set synonym relationship correctly
319 // SynonymRelationship relToTaxon = null;
320 boolean relToTargetTaxonExists
= false;
321 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
322 for (SynonymRelationship rel
: existingRelations
){
323 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
324 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
325 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
326 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
327 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
328 rel
.setType(newRelationType
);
329 //TODO handle citation and microCitation
332 relToTargetTaxonExists
= true;
334 if (removeFromOtherTaxa
){
335 acceptedTaxon
.removeSynonym(synonym
, false);
341 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
342 Taxon acceptedTaxon
= targetTaxon
;
343 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
344 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
345 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
346 //TODO handle citation and microCitation
347 Reference citation
= null;
348 String microCitation
= null;
349 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
356 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
359 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
361 clazz
= TaxonBase
.class;
363 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
367 protected void setDao(ITaxonDao dao
) {
372 * @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)
374 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
375 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
377 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
378 if(numberOfResults
> 0) { // no point checking again
379 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
382 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
386 * @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)
388 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
389 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
391 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
392 if(numberOfResults
> 0) { // no point checking again
393 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
400 * @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)
402 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
403 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
405 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
406 if(numberOfResults
> 0) { // no point checking again
407 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
413 * @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)
415 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
416 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
418 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
419 if(numberOfResults
> 0) { // no point checking again
420 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
422 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
426 * @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)
428 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
429 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
431 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
432 if(numberOfResults
> 0) { // no point checking again
433 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
439 * @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)
441 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
442 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
444 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
445 if(numberOfResults
> 0) { // no point checking again
446 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
448 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
452 * @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)
454 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
455 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
457 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
458 if(numberOfResults
> 0) { // no point checking again
459 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
462 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
466 * @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)
468 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
469 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
471 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
472 if(numberOfResults
> 0) { // no point checking again
473 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
476 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
480 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
482 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
483 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
484 return t
.getHomotypicSynonymsByHomotypicGroup();
488 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
490 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
491 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
492 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
493 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
494 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
495 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
497 return heterotypicSynonymyGroups
;
500 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator
){
502 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
503 // Class<? extends TaxonBase> clazz = null;
504 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
505 // clazz = TaxonBase.class;
506 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
507 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
508 // } else if(configurator.isDoTaxa()) {
509 // clazz = Taxon.class;
510 // //propertyPath = configurator.getTaxonPropertyPath();
511 // } else if (configurator.isDoSynonyms()) {
512 // clazz = Synonym.class;
513 // //propertyPath = configurator.getSynonymPropertyPath();
517 result
= dao
.getTaxaByNameForEditor(configurator
.isDoTaxa(), configurator
.isDoSynonyms(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
522 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
524 public Pager
<IdentifiableEntity
> findTaxaAndNames(ITaxonServiceConfigurator configurator
) {
526 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
527 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
528 List
<TaxonBase
> taxa
= null;
531 long numberTaxaResults
= 0L;
534 List
<String
> propertyPath
= new ArrayList
<String
>();
535 if(configurator
.getTaxonPropertyPath() != null){
536 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
540 if (configurator
.isDoMisappliedNames() || configurator
.isDoSynonyms() || configurator
.isDoTaxa()){
541 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
543 dao
.countTaxaByName(configurator
.isDoTaxa(),configurator
.isDoSynonyms(), configurator
.isDoMisappliedNames(),
544 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
545 configurator
.getNamedAreas());
548 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
549 taxa
= dao
.getTaxaByName(configurator
.isDoTaxa(), configurator
.isDoSynonyms(),
550 configurator
.isDoMisappliedNames(), configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(),
551 configurator
.getMatchMode(), configurator
.getNamedAreas(),
552 configurator
.getPageSize(), configurator
.getPageNumber(), propertyPath
);
556 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
559 results
.addAll(taxa
);
562 numberOfResults
+= numberTaxaResults
;
564 // Names without taxa
565 if (configurator
.isDoNamesWithoutTaxa()) {
566 int numberNameResults
= 0;
568 List
<?
extends TaxonNameBase
<?
,?
>> names
=
569 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
570 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
571 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
572 if (names
.size() > 0) {
573 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
574 if (taxonName
.getTaxonBases().size() == 0) {
575 results
.add(taxonName
);
579 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
580 numberOfResults
+= numberNameResults
;
584 // Taxa from common names
586 if (configurator
.isDoTaxaByCommonNames()) {
587 taxa
= new ArrayList
<TaxonBase
>();
588 numberTaxaResults
= 0;
589 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
590 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
592 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
593 List
<Object
[]> commonNameResults
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
594 for( Object
[] entry
: commonNameResults
) {
595 taxa
.add((TaxonBase
) entry
[0]);
599 results
.addAll(taxa
);
601 numberOfResults
+= numberTaxaResults
;
605 return new DefaultPagerImpl
<IdentifiableEntity
>
606 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
609 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
610 return dao
.getUuidAndTitleCache();
614 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
616 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
617 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
618 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
619 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
620 for (TaxonDescription taxDesc
: descriptions
){
621 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
622 for (DescriptionElementBase descElem
: elements
){
623 for(Media media
: descElem
.getMedia()){
625 //find the best matching representation
626 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
635 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
637 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
638 return this.dao
.findById(listOfIDs
);
642 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
644 public int countAllRelationships() {
645 return this.dao
.countAllRelationships();
652 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
654 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
655 return this.dao
.findIdenticalTaxonNames(propertyPath
);
660 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
663 public void deleteTaxon(Taxon taxon
, TaxonDeletionConfigurator config
) throws ReferencedObjectUndeletableException
{
665 config
= new TaxonDeletionConfigurator();
669 if (! config
.isDeleteTaxonNodes()){
670 if (taxon
.getTaxonNodes().size() > 0){
671 String message
= "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
672 throw new ReferencedObjectUndeletableException(message
);
677 // SynonymRelationShip
678 if (config
.isDeleteSynonymRelations()){
679 boolean removeSynonymNameFromHomotypicalGroup
= false;
680 for (SynonymRelationship synRel
: taxon
.getSynonymRelations()){
681 Synonym synonym
= synRel
.getSynonym();
682 taxon
.removeSynonymRelation(synRel
, removeSynonymNameFromHomotypicalGroup
);
683 if (config
.isDeleteSynonymsIfPossible()){
685 boolean newHomotypicGroupIfNeeded
= true;
686 deleteSynonym(synonym
, taxon
, config
.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded
);
688 deleteSynonymRelationships(synonym
, taxon
);
694 if (! config
.isDeleteTaxonRelationships()){
695 if (taxon
.getTaxonRelations().size() > 0){
696 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.";
697 throw new ReferencedObjectUndeletableException(message
);
703 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
705 for (TaxonDescription desc
: descriptions
){
706 if (config
.isDeleteDescriptions()){
707 //TODO use description delete configurator ?
708 //FIXME check if description is ALWAYS deletable
709 descriptionService
.delete(desc
);
711 if (desc
.getDescribedSpecimenOrObservations().size()>0){
712 String message
= "Taxon can't be deleted as it is used in a TaxonDescription" +
713 " which also describes specimens or abservations";
714 throw new ReferencedObjectUndeletableException(message
);
720 //check references with only reverse mapping
721 Set
<CdmBase
> referencingObjects
= genericDao
.getReferencingObjects(taxon
);
722 for (CdmBase referencingObject
: referencingObjects
){
723 //IIdentificationKeys (Media, Polytomous, MultiAccess)
724 if (HibernateProxyHelper
.isInstanceOf(referencingObject
, IIdentificationKey
.class)){
725 String message
= "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
726 message
= String
.format(message
, CdmBase
.deproxy(referencingObject
, DerivedUnitBase
.class).getTitleCache());
727 throw new ReferencedObjectUndeletableException(message
);
732 if (referencingObject
.isInstanceOf(PolytomousKeyNode
.class)){
733 String message
= "Taxon can't be deleted as it is used in polytomous key node";
734 throw new ReferencedObjectUndeletableException(message
);
738 if (referencingObject
.isInstanceOf(TaxonInteraction
.class)){
739 String message
= "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
740 throw new ReferencedObjectUndeletableException(message
);
746 if (config
.isDeleteNameIfPossible()){
748 nameService
.delete(taxon
.getName(), config
.getNameDeletionConfig());
749 } catch (ReferencedObjectUndeletableException e
) {
751 if (logger
.isDebugEnabled()){logger
.debug("Name could not be deleted");}
758 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
760 @Transactional(readOnly
= false)
762 public void deleteSynonym(Synonym synonym
, Taxon taxon
, boolean removeNameIfPossible
,boolean newHomotypicGroupIfNeeded
) {
763 if (synonym
== null){
766 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
768 //remove synonymRelationship
769 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
773 taxonSet
.addAll(synonym
.getAcceptedTaxa());
775 for (Taxon relatedTaxon
: taxonSet
){
776 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
777 relatedTaxon
.removeSynonym(synonym
, newHomotypicGroupIfNeeded
);
779 this.saveOrUpdate(synonym
);
781 //TODO remove name from homotypical group?
783 //remove synonym (if necessary)
784 if (synonym
.getSynonymRelations().isEmpty()){
785 TaxonNameBase
<?
,?
> name
= synonym
.getName();
786 synonym
.setName(null);
789 //remove name if possible (and required)
790 if (name
!= null && removeNameIfPossible
){
792 nameService
.delete(name
, new NameDeletionConfigurator());
793 }catch (DataChangeNoRollbackException ex
){
794 if (logger
.isDebugEnabled())logger
.debug("Name wasn't deleted as it is referenced");
802 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
804 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
806 return this.dao
.findIdenticalNamesNew(propertyPath
);
810 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
812 public String
getPhylumName(TaxonNameBase name
){
813 return this.dao
.getPhylumName(name
);
817 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
819 public long deleteSynonymRelationships(Synonym syn
, Taxon taxon
) {
820 return dao
.deleteSynonymRelationships(syn
, taxon
);
824 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
826 public long deleteSynonymRelationships(Synonym syn
) {
827 return dao
.deleteSynonymRelationships(syn
, null);
832 * @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)
834 public List
<SynonymRelationship
> listSynonymRelationships(
835 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
836 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
837 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
839 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
840 if(numberOfResults
> 0) { // no point checking again
841 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
847 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
850 public Taxon
findBestMatchingTaxon(String taxonName
) {
851 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
852 config
.setTaxonNameTitle(taxonName
);
853 return findBestMatchingTaxon(config
);
859 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
861 Taxon bestCandidate
= null;
863 // 1. search for acceptet taxa
864 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(true, false, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
865 boolean bestCandidateMatchesSecUuid
= false;
866 boolean bestCandidateIsInClassification
= false;
867 int countEqualCandidates
= 0;
868 for(TaxonBase taxonBaseCandidate
: taxonList
){
869 if(taxonBaseCandidate
instanceof Taxon
){
870 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
871 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
872 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
874 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
875 bestCandidate
= newCanditate
;
876 countEqualCandidates
= 1;
877 bestCandidateMatchesSecUuid
= true;
881 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
882 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
884 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
885 bestCandidate
= newCanditate
;
886 countEqualCandidates
= 1;
887 bestCandidateIsInClassification
= true;
890 if (bestCandidate
== null){
891 bestCandidate
= newCanditate
;
892 countEqualCandidates
= 1;
896 }else{ //not Taxon.class
899 countEqualCandidates
++;
902 if (bestCandidate
!= null){
903 if(countEqualCandidates
> 1){
904 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
905 return bestCandidate
;
907 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
908 return bestCandidate
;
913 // 2. search for synonyms
914 if (config
.isIncludeSynonyms()){
915 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
916 for(TaxonBase taxonBase
: synonymList
){
917 if(taxonBase
instanceof Synonym
){
918 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
919 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
920 if(!acceptetdCandidates
.isEmpty()){
921 bestCandidate
= acceptetdCandidates
.iterator().next();
922 if(acceptetdCandidates
.size() == 1){
923 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
924 return bestCandidate
;
926 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
927 return bestCandidate
;
929 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
935 } catch (Exception e
){
939 return bestCandidate
;
942 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
943 UUID configClassificationUuid
= config
.getClassificationUuid();
944 if (configClassificationUuid
== null){
947 for (TaxonNode node
: taxon
.getTaxonNodes()){
948 UUID classUuid
= node
.getClassification().getUuid();
949 if (configClassificationUuid
.equals(classUuid
)){
956 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
957 UUID configSecUuid
= config
.getSecUuid();
958 if (configSecUuid
== null){
961 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
962 return configSecUuid
.equals(taxonSecUuid
);
966 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
969 public Synonym
findBestMatchingSynonym(String taxonName
) {
970 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(false, true, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
971 if(! synonymList
.isEmpty()){
972 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
973 if(synonymList
.size() == 1){
974 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
977 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
986 * @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)
989 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
990 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
992 Synonym synonym
= oldSynonymRelation
.getSynonym();
993 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
994 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
995 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
996 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
997 //set default relationship type
998 if (newSynonymRelationshipType
== null){
999 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
1001 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
1003 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
1004 int hgSize
= homotypicGroup
.getTypifiedNames().size();
1005 boolean isSingleInGroup
= !(hgSize
> 1);
1007 if (! isSingleInGroup
){
1008 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
1009 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
1010 if (isHomotypicToAccepted
){
1011 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.";
1012 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
1013 message
= String
.format(message
, homotypicRelatives
);
1014 throw new HomotypicalGroupChangeException(message
);
1016 if (! moveHomotypicGroup
){
1017 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.";
1018 throw new HomotypicalGroupChangeException(message
);
1021 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
1023 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1025 SynonymRelationship result
= null;
1026 //move all synonyms to new taxon
1027 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
1028 for (Synonym syn
: homotypicSynonyms
){
1029 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
1030 for (SynonymRelationship synRelation
: synRelations
){
1031 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
1032 Reference
<?
> newReference
= reference
;
1033 if (newReference
== null && keepReference
){
1034 newReference
= synRelation
.getCitation();
1036 String newRefDetail
= referenceDetail
;
1037 if (newRefDetail
== null && keepReference
){
1038 newRefDetail
= synRelation
.getCitationMicroReference();
1040 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
1041 fromTaxon
.removeSynonymRelation(synRelation
, false);
1043 //change homotypic group of synonym if relType is 'homotypic'
1044 // if (newRelTypeIsHomotypic){
1045 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1048 if (synRelation
.equals(oldSynonymRelation
)){
1049 result
= newSynRelation
;
1055 saveOrUpdate(newTaxon
);
1056 //Assert that there is a result
1057 if (result
== null){
1058 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
1059 throw new IllegalStateException(message
);
1065 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1068 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
1069 return dao
.getUuidAndTitleCacheTaxon();
1073 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1076 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
1077 return dao
.getUuidAndTitleCacheSynonym();
1081 public Pager
<SearchResult
<TaxonBase
>> findByDescriptionElementFullText(Class
<?
extends DescriptionElementBase
> clazz
, String queryString
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
1082 List
<String
> propertyPaths
) throws CorruptIndexException
, IOException
, ParseException
{
1084 String luceneQueryTemplate
= "titleCache:%1$s OR multilanguageText.text:%1$s OR name:%1$s";
1085 String luceneQuery
= String
.format(luceneQueryTemplate
, queryString
);
1087 LuceneSearch luceneSearch
= new LuceneSearch(getSession(), clazz
);
1088 TopDocs topDocsResultSet
= luceneSearch
.executeSearch(luceneQuery
);
1089 List
<SearchResult
<TaxonBase
>> searchResults
= searchResultBuilder
.createResultSetFromIds(luceneSearch
, topDocsResultSet
, dao
, "inDescription.taxon.id");
1091 return new DefaultPagerImpl
<SearchResult
<TaxonBase
>>(pageNumber
, searchResults
.size(), pageSize
, searchResults
);
1095 public List
<Synonym
> createInferredSynonyms(Taxon taxon
, Classification classification
, SynonymRelationshipType type
){
1096 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
1097 List
<Synonym
> inferredSynonymsToBeRemoved
= new ArrayList
<Synonym
>();
1099 HashMap
<UUID
, ZoologicalName
> zooHashMap
= new HashMap
<UUID
, ZoologicalName
>();
1101 UUID uuid
= taxon
.getName().getUuid();
1102 ZoologicalName taxonName
= getZoologicalName(uuid
, zooHashMap
);
1103 String epithetOfTaxon
= null;
1104 String infragenericEpithetOfTaxon
= null;
1105 String infraspecificEpithetOfTaxon
= null;
1106 if (taxonName
.isSpecies()){
1107 epithetOfTaxon
= taxonName
.getSpecificEpithet();
1108 } else if (taxonName
.isInfraGeneric()){
1109 infragenericEpithetOfTaxon
= taxonName
.getInfraGenericEpithet();
1110 } else if (taxonName
.isInfraSpecific()){
1111 infraspecificEpithetOfTaxon
= taxonName
.getInfraSpecificEpithet();
1113 String genusOfTaxon
= taxonName
.getGenusOrUninomial();
1114 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
1115 List
<String
> taxonNames
= new ArrayList
<String
>();
1117 for (TaxonNode node
: nodes
){
1118 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1119 // List<String> synonymsEpithet = new ArrayList<String>();
1121 if (node
.getClassification().equals(classification
)){
1122 if (!node
.isTopmostNode()){
1123 TaxonNode parent
= (TaxonNode
)node
.getParent();
1124 parent
= (TaxonNode
)HibernateProxyHelper
.deproxy(parent
);
1125 TaxonNameBase parentName
= parent
.getTaxon().getName();
1126 ZoologicalName zooParentName
= HibernateProxyHelper
.deproxy(parentName
, ZoologicalName
.class);
1127 Taxon parentTaxon
= (Taxon
)HibernateProxyHelper
.deproxy(parent
.getTaxon());
1128 Rank rankOfTaxon
= taxonName
.getRank();
1131 //create inferred synonyms for species, subspecies or subgenus
1132 if (parentName
.isGenus() || parentName
.isSpecies() || parentName
.getRank().equals(Rank
.SUBGENUS())){
1134 Synonym inferredEpithet
;
1135 Synonym inferredGenus
= null;
1136 Synonym potentialCombination
;
1138 List
<String
> propertyPaths
= new ArrayList
<String
>();
1139 propertyPaths
.add("synonym");
1140 propertyPaths
.add("synonym.name");
1141 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
1142 orderHints
.add(new OrderHint("relatedFrom.titleCache", SortOrder
.ASCENDING
));
1144 List
<SynonymRelationship
> synonymRelationshipsOfGenus
= dao
.getSynonyms(parentTaxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1145 List
<SynonymRelationship
> synonymRelationshipsOfTaxon
= dao
.getSynonyms(taxon
, SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints
,propertyPaths
);
1147 if (type
.equals(SynonymRelationshipType
.INFERRED_EPITHET_OF())){
1149 for (SynonymRelationship synonymRelationOfGenus
:synonymRelationshipsOfGenus
){
1150 TaxonNameBase synName
;
1151 ZoologicalName inferredSynName
;
1152 Synonym syn
= synonymRelationOfGenus
.getSynonym();
1153 HibernateProxyHelper
.deproxy(syn
);
1155 // Determine the idInSource
1156 String idInSourceSyn
= getIdInSource(syn
);
1157 String idInSourceTaxon
= getIdInSource(taxon
);
1158 // Determine the sourceReference
1159 Reference sourceReference
= syn
.getSec();
1161 synName
= syn
.getName();
1162 ZoologicalName zooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
1163 String synGenusName
= zooName
.getGenusOrUninomial();
1164 String synInfraGenericEpithet
= null;
1165 String synSpecificEpithet
= null;
1166 if (zooName
.isInfraGeneric()){
1167 synInfraGenericEpithet
= zooName
.getInfraGenericEpithet();
1168 } else if (zooName
.isSpecies()){
1169 synSpecificEpithet
= zooName
.getSpecificEpithet();
1172 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1173 synonymsGenus.put(synGenusName, idInSource);
1176 inferredSynName
= ZoologicalName
.NewInstance(rankOfTaxon
);
1178 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1179 if (epithetOfTaxon
== null && infragenericEpithetOfTaxon
== null && infraspecificEpithetOfTaxon
== null) {
1180 logger
.error("This specificEpithet is NULL" + taxon
.getTitleCache());
1182 inferredSynName
.setGenusOrUninomial(synGenusName
);
1183 if (inferredSynName
.isSpecies()){
1184 if (parentName
.isInfraGeneric()){
1185 inferredSynName
.setInfraGenericEpithet(synInfraGenericEpithet
);
1187 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
1188 } else if (inferredSynName
.isInfraGeneric()) {
1189 inferredSynName
.setInfraGenericEpithet(infragenericEpithetOfTaxon
);
1190 } else if (inferredSynName
.isInfraSpecific()){
1191 inferredSynName
.setSpecificEpithet(synSpecificEpithet
);
1192 inferredSynName
.setInfraSpecificEpithet(infraspecificEpithetOfTaxon
);
1195 inferredEpithet
= Synonym
.NewInstance(inferredSynName
, null);
1197 // Set the sourceReference
1198 inferredEpithet
.setSec(sourceReference
);
1200 /* Add the original source
1201 if (idInSource != null) {
1202 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1205 Reference citation = getCitation(syn);
1206 if (citation != null) {
1207 originalSource.setCitation(citation);
1208 inferredEpithet.addSource(originalSource);
1211 String taxonId
= idInSourceTaxon
+ "; " + idInSourceSyn
;
1213 IdentifiableSource originalSource
;
1214 originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
1216 inferredEpithet
.addSource(originalSource
);
1218 originalSource
= IdentifiableSource
.NewInstance(taxonId
, INFERRED_EPITHET_NAMESPACE
, sourceReference
, null);
1220 inferredSynName
.addSource(originalSource
);
1224 taxon
.addSynonym(inferredEpithet
, SynonymRelationshipType
.INFERRED_EPITHET_OF());
1225 inferredSynonyms
.add(inferredEpithet
);
1226 inferredSynName
.generateTitle();
1227 zooHashMap
.put(inferredSynName
.getUuid(), inferredSynName
);
1228 taxonNames
.add(inferredSynName
.getNameCache());
1231 if (!taxonNames
.isEmpty()){
1232 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1233 ZoologicalName name
;
1234 if (!synNotInCDM
.isEmpty()){
1235 inferredSynonymsToBeRemoved
.clear();
1237 for (Synonym syn
:inferredSynonyms
){
1238 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1239 if (!synNotInCDM
.contains(name
.getNameCache())){
1240 inferredSynonymsToBeRemoved
.add(syn
);
1244 // Remove identified Synonyms from inferredSynonyms
1245 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
1246 inferredSynonyms
.remove(synonym
);
1251 }else if (type
.equals(SynonymRelationshipType
.INFERRED_GENUS_OF())){
1254 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
1255 TaxonNameBase synName
;
1256 ZoologicalName inferredSynName
;
1258 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
1259 synName
=syn
.getName();
1260 HibernateProxyHelper
.deproxy(syn
);
1262 // Determine the idInSource
1263 String idInSourceSyn
= getIdInSource(syn
);
1264 String idInSourceTaxon
= getIdInSource(taxon
);
1265 // Determine the sourceReference
1266 Reference sourceReference
= syn
.getSec();
1268 synName
= syn
.getName();
1269 ZoologicalName synZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
1270 String synSpeciesEpithetName
= synZooName
.getSpecificEpithet();
1271 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1272 synonymsEpithet.add(synSpeciesEpithetName);
1275 inferredSynName
= ZoologicalName
.NewInstance(rankOfTaxon
);
1276 //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...
1279 inferredSynName
.setGenusOrUninomial(genusOfTaxon
);
1281 if (taxonName
.isSpecies()){
1282 inferredSynName
.setSpecificEpithet(synSpeciesEpithetName
);
1283 if (zooParentName
.isInfraGeneric()){
1284 inferredSynName
.setInfraGenericEpithet(zooParentName
.getInfraGenericEpithet());
1287 if (taxonName
.isInfraSpecific()){
1288 inferredSynName
.setSpecificEpithet(epithetOfTaxon
);
1289 inferredSynName
.setInfraSpecificEpithet(synZooName
.getInfraGenericEpithet());
1291 if (taxonName
.isInfraGeneric()){
1292 inferredSynName
.setInfraGenericEpithet(synZooName
.getInfraGenericEpithet());
1295 inferredGenus
= Synonym
.NewInstance(inferredSynName
, null);
1297 // Set the sourceReference
1298 inferredGenus
.setSec(sourceReference
);
1300 // Add the original source
1301 if (idInSourceSyn
!= null && idInSourceTaxon
!= null) {
1302 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
1303 inferredGenus
.addSource(originalSource
);
1305 originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceTaxon
, INFERRED_GENUS_NAMESPACE
, sourceReference
, null);
1306 inferredSynName
.addSource(originalSource
);
1310 taxon
.addSynonym(inferredGenus
, SynonymRelationshipType
.INFERRED_GENUS_OF());
1311 inferredSynonyms
.add(inferredGenus
);
1312 inferredSynName
.generateTitle();
1313 zooHashMap
.put(inferredSynName
.getUuid(), inferredSynName
);
1314 taxonNames
.add(inferredSynName
.getNameCache());
1317 if (!taxonNames
.isEmpty()){
1318 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1319 ZoologicalName name
;
1320 if (!synNotInCDM
.isEmpty()){
1321 inferredSynonymsToBeRemoved
.clear();
1323 for (Synonym syn
:inferredSynonyms
){
1324 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1325 if (!synNotInCDM
.contains(name
.getNameCache())){
1326 inferredSynonymsToBeRemoved
.add(syn
);
1330 // Remove identified Synonyms from inferredSynonyms
1331 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
1332 inferredSynonyms
.remove(synonym
);
1337 }else if (type
.equals(SynonymRelationshipType
.POTENTIAL_COMBINATION_OF())){
1339 Reference sourceReference
= null; // TODO: Determination of sourceReference is redundant
1340 ZoologicalName inferredSynName
;
1341 //for all synonyms of the parent...
1342 for (SynonymRelationship synonymRelationOfGenus
:synonymRelationshipsOfGenus
){
1343 TaxonNameBase synName
;
1344 Synonym synParent
= synonymRelationOfGenus
.getSynonym();
1345 synName
= synParent
.getName();
1347 HibernateProxyHelper
.deproxy(synParent
);
1349 // Set the sourceReference
1350 sourceReference
= synParent
.getSec();
1352 // Determine the idInSource
1353 String idInSourceParent
= getIdInSource(synParent
);
1355 ZoologicalName parentZooName
= getZoologicalName(synName
.getUuid(), zooHashMap
);
1356 String synParentGenus
= parentZooName
.getGenusOrUninomial();
1357 String synParentInfragenericName
= null;
1358 String synParentSpecificEpithet
= null;
1360 if (parentZooName
.isInfraGeneric()){
1361 synParentInfragenericName
= parentZooName
.getInfraGenericEpithet();
1363 if (parentZooName
.isSpecies()){
1364 synParentSpecificEpithet
= parentZooName
.getSpecificEpithet();
1367 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1368 synonymsGenus.put(synGenusName, idInSource);
1371 //for all synonyms of the taxon
1373 for (SynonymRelationship synonymRelationOfTaxon
:synonymRelationshipsOfTaxon
){
1375 Synonym syn
= synonymRelationOfTaxon
.getSynonym();
1376 HibernateProxyHelper
.deproxy(syn
);
1378 // Set sourceReference
1379 sourceReference
= syn
.getSec();
1381 ZoologicalName zooName
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1382 String synTaxonSpecificEpithet
= zooName
.getSpecificEpithet();
1383 String synTaxonInfragenericName
= null;
1384 String synTaxonInfraSpecificName
= null;
1386 if (parentZooName
.isSpecies()){
1387 synTaxonInfraSpecificName
= zooName
.getInfraSpecificEpithet();
1389 if (zooName
.isInfraGeneric()){
1390 synTaxonInfragenericName
= zooName
.getInfraGenericEpithet();
1393 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1394 synonymsEpithet.add(epithetName);
1397 //create potential combinations...
1398 inferredSynName
= ZoologicalName
.NewInstance(rankOfTaxon
);
1400 inferredSynName
.setGenusOrUninomial(synParentGenus
);
1401 if (taxonName
.isSpecies()){
1402 inferredSynName
.setSpecificEpithet(synTaxonSpecificEpithet
);
1403 if (parentName
.isInfraGeneric()){
1404 inferredSynName
.setInfraGenericEpithet(synTaxonInfragenericName
);
1407 if (taxonName
.isInfraSpecific()){
1408 inferredSynName
.setSpecificEpithet(synParentSpecificEpithet
);
1409 inferredSynName
.setInfraSpecificEpithet(synTaxonInfraSpecificName
);
1411 if (taxonName
.isInfraGeneric()){
1412 inferredSynName
.setInfraGenericEpithet(synTaxonInfragenericName
);
1416 potentialCombination
= Synonym
.NewInstance(inferredSynName
, null);
1418 // Set the sourceReference
1419 potentialCombination
.setSec(sourceReference
);
1422 // Determine the idInSource
1423 String idInSourceSyn
= getIdInSource(syn
);
1425 if (idInSourceParent
!= null && idInSourceSyn
!= null) {
1426 // IdentifiableSource originalSourceGenus = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1427 // inferredGenus.addSource(originalSourceGenus);
1429 IdentifiableSource originalSource
= IdentifiableSource
.NewInstance(idInSourceSyn
+ "; " + idInSourceParent
, POTENTIAL_COMBINATION_NAMESPACE
, sourceReference
, null);
1430 inferredSynName
.addSource(originalSource
);
1434 // if (idInSourceTaxon != null) {
1435 // IdentifiableSource originalSourceTaxon = IdentifiableSource.NewInstance(idInSourceTaxon, "PotentialCombinationOf", sourceReference, null);
1436 // if (sourceReference != null) {
1437 // originalSourceTaxon.setCitation(sourceReference);
1438 // potentialCombination.addSource(originalSourceTaxon);
1441 // if (idInSourceParent != null){
1442 // IdentifiableSource originalSourceParent = IdentifiableSource.NewInstance(idInSourceParent, "PotentialCombinationOf", sourceReference, null);
1443 // if (sourceReference != null) {
1444 // originalSourceParent.setCitation(sourceReference);
1445 // potentialCombination.addSource(originalSourceParent);
1453 inferredSynonyms
.add(potentialCombination
);
1454 inferredSynName
.generateTitle();
1455 zooHashMap
.put(inferredSynName
.getUuid(), inferredSynName
);
1456 taxonNames
.add(inferredSynName
.getNameCache());
1461 if (!taxonNames
.isEmpty()){
1462 List
<String
> synNotInCDM
= dao
.taxaByNameNotInDB(taxonNames
);
1463 ZoologicalName name
;
1464 if (!synNotInCDM
.isEmpty()){
1465 inferredSynonymsToBeRemoved
.clear();
1466 for (Synonym syn
:inferredSynonyms
){
1468 name
= (ZoologicalName
) syn
.getName();
1469 }catch (ClassCastException e
){
1470 name
= getZoologicalName(syn
.getName().getUuid(), zooHashMap
);
1472 if (!synNotInCDM
.contains(name
.getNameCache())){
1473 inferredSynonymsToBeRemoved
.add(syn
);
1476 // Remove identified Synonyms from inferredSynonyms
1477 for (Synonym synonym
: inferredSynonymsToBeRemoved
) {
1478 inferredSynonyms
.remove(synonym
);
1484 logger
.info("The synonymrelationship type is not defined.");
1485 return inferredSynonyms
;
1492 return inferredSynonyms
;
1496 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1497 * Very likely only useful for createInferredSynonyms().
1502 private ZoologicalName
getZoologicalName(UUID uuid
, HashMap
<UUID
, ZoologicalName
> zooHashMap
) {
1503 ZoologicalName taxonName
=nameDao
.findZoologicalNameByUUID(uuid
);
1504 if (taxonName
== null) {
1505 taxonName
= zooHashMap
.get(uuid
);
1511 * Returns the idInSource for a given Synonym.
1514 private String
getIdInSource(TaxonBase taxonBase
) {
1515 String idInSource
= null;
1516 Set
<IdentifiableSource
> sources
= taxonBase
.getSources();
1517 if (sources
.size() == 1) {
1518 IdentifiableSource source
= sources
.iterator().next();
1519 if (source
!= null) {
1520 idInSource
= source
.getIdInSource();
1522 } else if (sources
.size() > 1) {
1525 for (IdentifiableSource source
: sources
) {
1526 idInSource
+= source
.getIdInSource();
1527 if (count
< sources
.size()) {
1539 * Returns the citation for a given Synonym.
1542 private Reference
getCitation(Synonym syn
) {
1543 Reference citation
= null;
1544 Set
<IdentifiableSource
> sources
= syn
.getSources();
1545 if (sources
.size() == 1) {
1546 IdentifiableSource source
= sources
.iterator().next();
1547 if (source
!= null) {
1548 citation
= source
.getCitation();
1550 } else if (sources
.size() > 1) {
1551 logger
.warn("This Synonym has more than one source: " + syn
.getUuid() + " (" + syn
.getTitleCache() +")");
1557 public List
<Synonym
> createAllInferredSynonyms(Taxon taxon
, Classification tree
){
1558 List
<Synonym
> inferredSynonyms
= new ArrayList
<Synonym
>();
1560 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_EPITHET_OF()));
1561 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.INFERRED_GENUS_OF()));
1562 inferredSynonyms
.addAll(createInferredSynonyms(taxon
, tree
, SynonymRelationshipType
.POTENTIAL_COMBINATION_OF()));
1564 return inferredSynonyms
;