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
.util
.ArrayList
;
14 import java
.util
.HashSet
;
15 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import junit
.framework
.Assert
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
23 import org
.springframework
.stereotype
.Service
;
24 import org
.springframework
.transaction
.annotation
.Propagation
;
25 import org
.springframework
.transaction
.annotation
.Transactional
;
27 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
28 import eu
.etaxonomy
.cdm
.api
.service
.config
.MatchingTaxonConfigurator
;
29 import eu
.etaxonomy
.cdm
.api
.service
.config
.NameDeletionConfigurator
;
30 import eu
.etaxonomy
.cdm
.api
.service
.exception
.DataChangeNoRollbackException
;
31 import eu
.etaxonomy
.cdm
.api
.service
.exception
.HomotypicalGroupChangeException
;
32 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
33 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
34 import eu
.etaxonomy
.cdm
.common
.monitor
.IProgressMonitor
;
35 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
36 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
37 import eu
.etaxonomy
.cdm
.model
.common
.OrderedTermVocabulary
;
38 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
;
39 import eu
.etaxonomy
.cdm
.model
.common
.RelationshipBase
.Direction
;
40 import eu
.etaxonomy
.cdm
.model
.common
.UuidAndTitleCache
;
41 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
42 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
43 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
44 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
45 import eu
.etaxonomy
.cdm
.model
.media
.MediaUtils
;
46 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
47 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
48 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
49 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
50 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
51 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
52 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
53 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
54 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
55 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
56 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
57 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
58 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
59 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IOrderedTermVocabularyDao
;
60 import eu
.etaxonomy
.cdm
.persistence
.dao
.name
.ITaxonNameDao
;
61 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
62 import eu
.etaxonomy
.cdm
.persistence
.fetch
.CdmFetch
;
63 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
64 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
65 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
69 * @author a.kohlbecker
74 @Transactional(propagation
= Propagation
.SUPPORTS
, readOnly
= true)
75 public class TaxonServiceImpl
extends IdentifiableServiceBase
<TaxonBase
,ITaxonDao
> implements ITaxonService
{
76 private static final Logger logger
= Logger
.getLogger(TaxonServiceImpl
.class);
79 private ITaxonNameDao nameDao
;
82 private IOrderedTermVocabularyDao orderedVocabularyDao
;
85 private INameService nameService
;
90 public TaxonServiceImpl(){
91 if (logger
.isDebugEnabled()) { logger
.debug("Load TaxonService Bean"); }
95 * FIXME Candidate for harmonization
96 * rename searchByName ?
98 public List
<TaxonBase
> searchTaxaByName(String name
, Reference sec
) {
99 return dao
.getTaxaByName(name
, sec
);
103 * FIXME Candidate for harmonization
104 * list(Synonym.class, ...)
106 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
108 public List
<Synonym
> getAllSynonyms(int limit
, int start
) {
109 return dao
.getAllSynonyms(limit
, start
);
113 * FIXME Candidate for harmonization
114 * list(Taxon.class, ...)
116 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
118 public List
<Taxon
> getAllTaxa(int limit
, int start
) {
119 return dao
.getAllTaxa(limit
, start
);
123 * FIXME Candidate for harmonization
124 * merge with getRootTaxa(Reference sec, ..., ...)
126 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
128 public List
<Taxon
> getRootTaxa(Reference sec
, CdmFetch cdmFetch
, boolean onlyWithChildren
) {
129 if (cdmFetch
== null){
130 cdmFetch
= CdmFetch
.NO_FETCH();
132 return dao
.getRootTaxa(sec
, cdmFetch
, onlyWithChildren
, false);
137 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
139 public List
<Taxon
> getRootTaxa(Rank rank
, Reference sec
, boolean onlyWithChildren
,boolean withMisapplications
, List
<String
> propertyPaths
) {
140 return dao
.getRootTaxa(rank
, sec
, null, onlyWithChildren
, withMisapplications
, propertyPaths
);
144 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
146 public List
<RelationshipBase
> getAllRelationships(int limit
, int start
){
147 return dao
.getAllRelationships(limit
, start
);
151 * FIXME Candidate for harmonization
152 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
155 public OrderedTermVocabulary
<TaxonRelationshipType
> getTaxonRelationshipTypeVocabulary() {
157 String taxonRelTypeVocabularyId
= "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
158 UUID uuid
= UUID
.fromString(taxonRelTypeVocabularyId
);
159 OrderedTermVocabulary
<TaxonRelationshipType
> taxonRelTypeVocabulary
=
160 (OrderedTermVocabulary
)orderedVocabularyDao
.findByUuid(uuid
);
161 return taxonRelTypeVocabulary
;
168 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
170 @Transactional(readOnly
= false)
171 public void swapSynonymAndAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
){
173 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
174 synonymName
.removeTaxonBase(synonym
);
175 TaxonNameBase
<?
,?
> taxonName
= acceptedTaxon
.getName();
176 taxonName
.removeTaxonBase(acceptedTaxon
);
178 synonym
.setName(taxonName
);
179 acceptedTaxon
.setName(synonymName
);
181 // the accepted taxon needs a new uuid because the concept has changed
182 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
183 //acceptedTaxon.setUuid(UUID.randomUUID());
188 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
190 //TODO correct delete handling still needs to be implemented / checked
192 @Transactional(readOnly
= false)
193 public Taxon
changeSynonymToAcceptedTaxon(Synonym synonym
, Taxon acceptedTaxon
, boolean deleteSynonym
, boolean copyCitationInfo
, Reference citation
, String microCitation
) throws IllegalArgumentException
{
195 TaxonNameBase
<?
,?
> acceptedName
= acceptedTaxon
.getName();
196 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
197 HomotypicalGroup synonymHomotypicGroup
= synonymName
.getHomotypicalGroup();
199 //check synonym is not homotypic
200 if (acceptedName
.getHomotypicalGroup().equals(synonymHomotypicGroup
)){
201 String message
= "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
202 throw new IllegalArgumentException(message
);
205 Taxon newAcceptedTaxon
= Taxon
.NewInstance(synonymName
, acceptedTaxon
.getSec());
207 SynonymRelationshipType relTypeForGroup
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
208 List
<Synonym
> heteroSynonyms
= acceptedTaxon
.getSynonymsInGroup(synonymHomotypicGroup
);
210 for (Synonym heteroSynonym
: heteroSynonyms
){
211 if (synonym
.equals(heteroSynonym
)){
212 acceptedTaxon
.removeSynonym(heteroSynonym
, false);
214 //move synonyms in same homotypic group to new accepted taxon
215 heteroSynonym
.replaceAcceptedTaxon(newAcceptedTaxon
, relTypeForGroup
, copyCitationInfo
, citation
, microCitation
);
219 //synonym.getName().removeTaxonBase(synonym);
220 //TODO correct delete handling still needs to be implemented / checked
222 // deleteSynonym(synonym, taxon, false);
225 this.delete(synonym
);
227 } catch (Exception e
) {
228 logger
.info("Can't delete old synonym from database");
232 return newAcceptedTaxon
;
236 public Taxon
changeSynonymToRelatedTaxon(Synonym synonym
, Taxon toTaxon
, TaxonRelationshipType taxonRelationshipType
, Reference citation
, String microcitation
){
238 // Get name from synonym
239 TaxonNameBase
<?
, ?
> synonymName
= synonym
.getName();
241 // remove synonym from taxon
242 toTaxon
.removeSynonym(synonym
);
244 // Create a taxon with synonym name
245 Taxon fromTaxon
= Taxon
.NewInstance(synonymName
, null);
247 // Add taxon relation
248 fromTaxon
.addTaxonRelation(toTaxon
, taxonRelationshipType
, citation
, microcitation
);
250 // since we are swapping names, we have to detach the name from the synonym completely.
251 // Otherwise the synonym will still be in the list of typified names.
252 synonym
.getName().removeTaxonBase(synonym
);
259 * @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)
261 @Transactional(readOnly
= false)
263 public void changeHomotypicalGroupOfSynonym(Synonym synonym
, HomotypicalGroup newHomotypicalGroup
, Taxon targetTaxon
,
264 boolean removeFromOtherTaxa
, boolean setBasionymRelationIfApplicable
){
266 TaxonNameBase synonymName
= synonym
.getName();
267 HomotypicalGroup oldHomotypicalGroup
= synonymName
.getHomotypicalGroup();
271 oldHomotypicalGroup
.removeTypifiedName(synonymName
);
272 newHomotypicalGroup
.addTypifiedName(synonymName
);
274 //remove existing basionym relationships
275 synonymName
.removeBasionyms();
277 //add basionym relationship
278 if (setBasionymRelationIfApplicable
){
279 Set
<TaxonNameBase
> basionyms
= newHomotypicalGroup
.getBasionyms();
280 for (TaxonNameBase basionym
: basionyms
){
281 synonymName
.addBasionym(basionym
);
285 //set synonym relationship correctly
286 // SynonymRelationship relToTaxon = null;
287 boolean relToTargetTaxonExists
= false;
288 Set
<SynonymRelationship
> existingRelations
= synonym
.getSynonymRelations();
289 for (SynonymRelationship rel
: existingRelations
){
290 Taxon acceptedTaxon
= rel
.getAcceptedTaxon();
291 boolean isTargetTaxon
= acceptedTaxon
!= null && acceptedTaxon
.equals(targetTaxon
);
292 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
293 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
294 SynonymRelationshipType newRelationType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
295 rel
.setType(newRelationType
);
296 //TODO handle citation and microCitation
299 relToTargetTaxonExists
= true;
301 if (removeFromOtherTaxa
){
302 acceptedTaxon
.removeSynonym(synonym
, false);
308 if (targetTaxon
!= null && ! relToTargetTaxonExists
){
309 Taxon acceptedTaxon
= targetTaxon
;
310 HomotypicalGroup acceptedGroup
= acceptedTaxon
.getHomotypicGroup();
311 boolean isHomotypicToTaxon
= acceptedGroup
.equals(newHomotypicalGroup
);
312 SynonymRelationshipType relType
= isHomotypicToTaxon? SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
313 //TODO handle citation and microCitation
314 Reference citation
= null;
315 String microCitation
= null;
316 acceptedTaxon
.addSynonym(synonym
, relType
, citation
, microCitation
);
323 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
326 public void updateTitleCache(Class
<?
extends TaxonBase
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<TaxonBase
> cacheStrategy
, IProgressMonitor monitor
) {
328 clazz
= TaxonBase
.class;
330 super.updateTitleCacheImpl(clazz
, stepSize
, cacheStrategy
, monitor
);
334 protected void setDao(ITaxonDao dao
) {
339 * @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)
341 public Pager
<TaxonBase
> findTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
342 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
344 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
345 if(numberOfResults
> 0) { // no point checking again
346 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
349 return new DefaultPagerImpl
<TaxonBase
>(pageNumber
, numberOfResults
, pageSize
, results
);
353 * @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)
355 public List
<TaxonBase
> listTaxaByName(Class
<?
extends TaxonBase
> clazz
, String uninomial
, String infragenericEpithet
, String specificEpithet
, String infraspecificEpithet
, Rank rank
, Integer pageSize
,Integer pageNumber
) {
356 Integer numberOfResults
= dao
.countTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
);
358 List
<TaxonBase
> results
= new ArrayList
<TaxonBase
>();
359 if(numberOfResults
> 0) { // no point checking again
360 results
= dao
.findTaxaByName(clazz
, uninomial
, infragenericEpithet
, specificEpithet
, infraspecificEpithet
, rank
, pageSize
, pageNumber
);
367 * @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)
369 public List
<TaxonRelationship
> listToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
370 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
372 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
373 if(numberOfResults
> 0) { // no point checking again
374 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
380 * @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)
382 public Pager
<TaxonRelationship
> pageToTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
383 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedTo
);
385 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
386 if(numberOfResults
> 0) { // no point checking again
387 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedTo
);
389 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
393 * @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)
395 public List
<TaxonRelationship
> listFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
){
396 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
398 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
399 if(numberOfResults
> 0) { // no point checking again
400 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
406 * @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)
408 public Pager
<TaxonRelationship
> pageFromTaxonRelationships(Taxon taxon
, TaxonRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
409 Integer numberOfResults
= dao
.countTaxonRelationships(taxon
, type
, TaxonRelationship
.Direction
.relatedFrom
);
411 List
<TaxonRelationship
> results
= new ArrayList
<TaxonRelationship
>();
412 if(numberOfResults
> 0) { // no point checking again
413 results
= dao
.getTaxonRelationships(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, TaxonRelationship
.Direction
.relatedFrom
);
415 return new DefaultPagerImpl
<TaxonRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
419 * @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)
421 public Pager
<SynonymRelationship
> getSynonyms(Taxon taxon
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
422 Integer numberOfResults
= dao
.countSynonyms(taxon
, type
);
424 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
425 if(numberOfResults
> 0) { // no point checking again
426 results
= dao
.getSynonyms(taxon
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
429 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
433 * @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)
435 public Pager
<SynonymRelationship
> getSynonyms(Synonym synonym
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
436 Integer numberOfResults
= dao
.countSynonyms(synonym
, type
);
438 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
439 if(numberOfResults
> 0) { // no point checking again
440 results
= dao
.getSynonyms(synonym
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
443 return new DefaultPagerImpl
<SynonymRelationship
>(pageNumber
, numberOfResults
, pageSize
, results
);
447 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
449 public List
<Synonym
> getHomotypicSynonymsByHomotypicGroup(Taxon taxon
, List
<String
> propertyPaths
){
450 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
451 return t
.getHomotypicSynonymsByHomotypicGroup();
455 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
457 public List
<List
<Synonym
>> getHeterotypicSynonymyGroups(Taxon taxon
, List
<String
> propertyPaths
){
458 Taxon t
= (Taxon
)dao
.load(taxon
.getUuid(), propertyPaths
);
459 List
<HomotypicalGroup
> homotypicalGroups
= t
.getHeterotypicSynonymyGroups();
460 List
<List
<Synonym
>> heterotypicSynonymyGroups
= new ArrayList
<List
<Synonym
>>(homotypicalGroups
.size());
461 for(HomotypicalGroup homotypicalGroup
: homotypicalGroups
){
462 heterotypicSynonymyGroups
.add(t
.getSynonymsInGroup(homotypicalGroup
));
464 return heterotypicSynonymyGroups
;
467 public List
<UuidAndTitleCache
<TaxonBase
>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator
){
469 List
<UuidAndTitleCache
<TaxonBase
>> result
= new ArrayList
<UuidAndTitleCache
<TaxonBase
>>();
470 Class
<?
extends TaxonBase
> clazz
= null;
471 if ((configurator
.isDoTaxa() && configurator
.isDoSynonyms())) {
472 clazz
= TaxonBase
.class;
473 //propertyPath.addAll(configurator.getTaxonPropertyPath());
474 //propertyPath.addAll(configurator.getSynonymPropertyPath());
475 } else if(configurator
.isDoTaxa()) {
477 //propertyPath = configurator.getTaxonPropertyPath();
478 } else if (configurator
.isDoSynonyms()) {
479 clazz
= Synonym
.class;
480 //propertyPath = configurator.getSynonymPropertyPath();
484 result
= dao
.getTaxaByNameForEditor(clazz
, configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
489 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
491 public Pager
<IdentifiableEntity
> findTaxaAndNames(ITaxonServiceConfigurator configurator
) {
493 List
<IdentifiableEntity
> results
= new ArrayList
<IdentifiableEntity
>();
494 int numberOfResults
= 0; // overall number of results (as opposed to number of results per page)
495 List
<TaxonBase
> taxa
= null;
498 long numberTaxaResults
= 0L;
500 Class
<?
extends TaxonBase
> clazz
= null;
501 List
<String
> propertyPath
= new ArrayList
<String
>();
502 if(configurator
.getTaxonPropertyPath() != null){
503 propertyPath
.addAll(configurator
.getTaxonPropertyPath());
505 if ((configurator
.isDoTaxa() && configurator
.isDoSynonyms())) {
506 clazz
= TaxonBase
.class;
507 //propertyPath.addAll(configurator.getTaxonPropertyPath());
508 //propertyPath.addAll(configurator.getSynonymPropertyPath());
509 } else if(configurator
.isDoTaxa()) {
511 //propertyPath = configurator.getTaxonPropertyPath();
512 } else if (configurator
.isDoSynonyms()) {
513 clazz
= Synonym
.class;
514 //propertyPath = configurator.getSynonymPropertyPath();
518 if(configurator
.getPageSize() != null){ // no point counting if we need all anyway
520 dao
.countTaxaByName(clazz
,
521 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
522 configurator
.getNamedAreas());
525 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){ // no point checking again if less results
526 taxa
= dao
.getTaxaByName(clazz
,
527 configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(),
528 configurator
.getNamedAreas(), configurator
.getPageSize(),
529 configurator
.getPageNumber(), propertyPath
);
533 if (logger
.isDebugEnabled()) { logger
.debug(numberTaxaResults
+ " matching taxa counted"); }
536 results
.addAll(taxa
);
539 numberOfResults
+= numberTaxaResults
;
541 // Names without taxa
542 if (configurator
.isDoNamesWithoutTaxa()) {
543 int numberNameResults
= 0;
545 List
<?
extends TaxonNameBase
<?
,?
>> names
=
546 nameDao
.findByName(configurator
.getTitleSearchStringSqlized(), configurator
.getMatchMode(),
547 configurator
.getPageSize(), configurator
.getPageNumber(), null, configurator
.getTaxonNamePropertyPath());
548 if (logger
.isDebugEnabled()) { logger
.debug(names
.size() + " matching name(s) found"); }
549 if (names
.size() > 0) {
550 for (TaxonNameBase
<?
,?
> taxonName
: names
) {
551 if (taxonName
.getTaxonBases().size() == 0) {
552 results
.add(taxonName
);
556 if (logger
.isDebugEnabled()) { logger
.debug(numberNameResults
+ " matching name(s) without taxa found"); }
557 numberOfResults
+= numberNameResults
;
561 // Taxa from common names
563 if (configurator
.isDoTaxaByCommonNames()) {
565 numberTaxaResults
= 0;
566 if(configurator
.getPageSize() != null){// no point counting if we need all anyway
567 numberTaxaResults
= dao
.countTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas());
569 if(configurator
.getPageSize() == null || numberTaxaResults
> configurator
.getPageSize() * configurator
.getPageNumber()){
570 taxa
= dao
.getTaxaByCommonName(configurator
.getTitleSearchStringSqlized(), configurator
.getClassification(), configurator
.getMatchMode(), configurator
.getNamedAreas(), configurator
.getPageSize(), configurator
.getPageNumber(), configurator
.getTaxonPropertyPath());
573 results
.addAll(taxa
);
575 numberOfResults
+= numberTaxaResults
;
579 return new DefaultPagerImpl
<IdentifiableEntity
>
580 (configurator
.getPageNumber(), numberOfResults
, configurator
.getPageSize(), results
);
583 public List
<UuidAndTitleCache
<TaxonBase
>> getTaxonUuidAndTitleCache(){
584 return dao
.getUuidAndTitleCache();
588 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
590 public List
<MediaRepresentation
> getAllMedia(Taxon taxon
, int size
, int height
, int widthOrDuration
, String
[] mimeTypes
){
591 List
<MediaRepresentation
> medRep
= new ArrayList
<MediaRepresentation
>();
592 taxon
= (Taxon
)dao
.load(taxon
.getUuid());
593 Set
<TaxonDescription
> descriptions
= taxon
.getDescriptions();
594 for (TaxonDescription taxDesc
: descriptions
){
595 Set
<DescriptionElementBase
> elements
= taxDesc
.getElements();
596 for (DescriptionElementBase descElem
: elements
){
597 for(Media media
: descElem
.getMedia()){
599 //find the best matching representation
600 medRep
.add(MediaUtils
.findBestMatchingRepresentation(media
, null, size
, height
, widthOrDuration
, mimeTypes
));
609 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
611 public List
<TaxonBase
> findTaxaByID(Set
<Integer
> listOfIDs
) {
612 return this.dao
.findById(listOfIDs
);
616 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
618 public int countAllRelationships() {
619 return this.dao
.countAllRelationships();
623 * @see eu.etaxonomy.cdm.api.service.ITaxonService#createAllInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon)
625 public List
<Synonym
> createAllInferredSynonyms(Classification tree
,
628 return this.dao
.createAllInferredSynonyms(taxon
, tree
);
632 * @see eu.etaxonomy.cdm.api.service.ITaxonService#createInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType)
634 public List
<Synonym
> createInferredSynonyms(Classification tree
, Taxon taxon
, SynonymRelationshipType type
) {
635 return this.dao
.createInferredSynonyms(taxon
, tree
, type
);
639 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
641 public List
<TaxonNameBase
> findIdenticalTaxonNames(List
<String
> propertyPath
) {
642 return this.dao
.findIdenticalTaxonNames(propertyPath
);
647 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
649 @Transactional(readOnly
= false)
651 public void deleteSynonym(Synonym synonym
, Taxon taxon
, boolean removeNameIfPossible
,boolean newHomotypicGroupIfNeeded
) {
652 if (synonym
== null){
655 synonym
= CdmBase
.deproxy(dao
.merge(synonym
), Synonym
.class);
657 //remove synonymRelationship
658 Set
<Taxon
> taxonSet
= new HashSet
<Taxon
>();
662 taxonSet
.addAll(synonym
.getAcceptedTaxa());
664 for (Taxon relatedTaxon
: taxonSet
){
665 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
666 relatedTaxon
.removeSynonym(synonym
, newHomotypicGroupIfNeeded
);
668 this.saveOrUpdate(synonym
);
670 //TODO remove name from homotypical group?
672 //remove synonym (if necessary)
673 if (synonym
.getSynonymRelations().isEmpty()){
674 TaxonNameBase
<?
,?
> name
= synonym
.getName();
675 synonym
.setName(null);
678 //remove name if possible (and required)
679 if (name
!= null && removeNameIfPossible
){
681 nameService
.delete(name
, new NameDeletionConfigurator());
682 }catch (DataChangeNoRollbackException ex
){
683 if (logger
.isDebugEnabled())logger
.debug("Name wasn't deleted as it is referenced");
691 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
693 public List
<TaxonNameBase
> findIdenticalTaxonNameIds(List
<String
> propertyPath
) {
695 return this.dao
.findIdenticalNamesNew(propertyPath
);
699 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
701 public String
getPhylumName(TaxonNameBase name
){
702 return this.dao
.getPhylumName(name
);
706 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
708 public long deleteSynonymRelationships(Synonym syn
) {
709 return dao
.deleteSynonymRelationships(syn
, null);
714 * @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)
716 public List
<SynonymRelationship
> listSynonymRelationships(
717 TaxonBase taxonBase
, SynonymRelationshipType type
, Integer pageSize
, Integer pageNumber
,
718 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, Direction direction
) {
719 Integer numberOfResults
= dao
.countSynonymRelationships(taxonBase
, type
, direction
);
721 List
<SynonymRelationship
> results
= new ArrayList
<SynonymRelationship
>();
722 if(numberOfResults
> 0) { // no point checking again
723 results
= dao
.getSynonymRelationships(taxonBase
, type
, pageSize
, pageNumber
, orderHints
, propertyPaths
, direction
);
729 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
732 public Taxon
findBestMatchingTaxon(String taxonName
) {
733 MatchingTaxonConfigurator config
= MatchingTaxonConfigurator
.NewInstance();
734 config
.setTaxonNameTitle(taxonName
);
735 return findBestMatchingTaxon(config
);
741 public Taxon
findBestMatchingTaxon(MatchingTaxonConfigurator config
) {
743 Taxon bestCandidate
= null;
745 // 1. search for acceptet taxa
746 List
<TaxonBase
> taxonList
= dao
.findByNameTitleCache(Taxon
.class, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
747 boolean bestCandidateMatchesSecUuid
= false;
748 boolean bestCandidateIsInClassification
= false;
749 int countEqualCandidates
= 0;
750 for(TaxonBase taxonBaseCandidate
: taxonList
){
751 if(taxonBaseCandidate
instanceof Taxon
){
752 Taxon newCanditate
= CdmBase
.deproxy(taxonBaseCandidate
, Taxon
.class);
753 boolean newCandidateMatchesSecUuid
= isMatchesSecUuid(newCanditate
, config
);
754 if (! newCandidateMatchesSecUuid
&& config
.isOnlyMatchingSecUuid() ){
756 }else if(newCandidateMatchesSecUuid
&& ! bestCandidateMatchesSecUuid
){
757 bestCandidate
= newCanditate
;
758 countEqualCandidates
= 1;
759 bestCandidateMatchesSecUuid
= true;
763 boolean newCandidateInClassification
= isInClassification(newCanditate
, config
);
764 if (! newCandidateInClassification
&& config
.isOnlyMatchingClassificationUuid()){
766 }else if (newCandidateInClassification
&& ! bestCandidateIsInClassification
){
767 bestCandidate
= newCanditate
;
768 countEqualCandidates
= 1;
769 bestCandidateIsInClassification
= true;
772 if (bestCandidate
== null){
773 bestCandidate
= newCanditate
;
774 countEqualCandidates
= 1;
778 }else{ //not Taxon.class
781 countEqualCandidates
++;
784 if (bestCandidate
!= null){
785 if(countEqualCandidates
> 1){
786 logger
.info(countEqualCandidates
+ " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate
.getTitleCache());
787 return bestCandidate
;
789 logger
.info("using accepted Taxon: " + bestCandidate
.getTitleCache());
790 return bestCandidate
;
795 // 2. search for synonyms
796 if (config
.isIncludeSynonyms()){
797 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(Synonym
.class, config
.getTaxonNameTitle(), null, MatchMode
.EXACT
, null, 0, null, null);
798 for(TaxonBase taxonBase
: synonymList
){
799 if(taxonBase
instanceof Synonym
){
800 Synonym synonym
= CdmBase
.deproxy(taxonBase
, Synonym
.class);
801 Set
<Taxon
> acceptetdCandidates
= synonym
.getAcceptedTaxa();
802 if(!acceptetdCandidates
.isEmpty()){
803 bestCandidate
= acceptetdCandidates
.iterator().next();
804 if(acceptetdCandidates
.size() == 1){
805 logger
.info(acceptetdCandidates
.size() + " Accepted taxa found for synonym " + taxonBase
.getTitleCache() + ", using first one: " + bestCandidate
.getTitleCache());
806 return bestCandidate
;
808 logger
.info("using accepted Taxon " + bestCandidate
.getTitleCache() + "for synonym " + taxonBase
.getTitleCache());
809 return bestCandidate
;
811 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
817 } catch (Exception e
){
821 return bestCandidate
;
824 private boolean isInClassification(Taxon taxon
, MatchingTaxonConfigurator config
) {
825 UUID configClassificationUuid
= config
.getClassificationUuid();
826 if (configClassificationUuid
== null){
829 for (TaxonNode node
: taxon
.getTaxonNodes()){
830 UUID classUuid
= node
.getClassification().getUuid();
831 if (configClassificationUuid
.equals(classUuid
)){
838 private boolean isMatchesSecUuid(Taxon taxon
, MatchingTaxonConfigurator config
) {
839 UUID configSecUuid
= config
.getSecUuid();
840 if (configSecUuid
== null){
843 UUID taxonSecUuid
= (taxon
.getSec() == null)?
null : taxon
.getSec().getUuid();
844 return configSecUuid
.equals(taxonSecUuid
);
848 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
851 public Synonym
findBestMatchingSynonym(String taxonName
) {
852 List
<TaxonBase
> synonymList
= dao
.findByNameTitleCache(Synonym
.class, taxonName
, null, MatchMode
.EXACT
, null, 0, null, null);
853 if(! synonymList
.isEmpty()){
854 Synonym result
= CdmBase
.deproxy(synonymList
.iterator().next(), Synonym
.class);
855 if(synonymList
.size() == 1){
856 logger
.info(synonymList
.size() + " Synonym found " + result
.getTitleCache() );
859 logger
.info("Several matching synonyms found. Using first: " + result
.getTitleCache());
868 * @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)
871 public SynonymRelationship
moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation
, Taxon newTaxon
, boolean moveHomotypicGroup
,
872 SynonymRelationshipType newSynonymRelationshipType
, Reference reference
, String referenceDetail
, boolean keepReference
) throws HomotypicalGroupChangeException
{
874 Synonym synonym
= oldSynonymRelation
.getSynonym();
875 Taxon fromTaxon
= oldSynonymRelation
.getAcceptedTaxon();
876 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
877 TaxonNameBase
<?
,?
> synonymName
= synonym
.getName();
878 TaxonNameBase
<?
,?
> fromTaxonName
= fromTaxon
.getName();
879 //set default relationship type
880 if (newSynonymRelationshipType
== null){
881 newSynonymRelationshipType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
883 boolean newRelTypeIsHomotypic
= newSynonymRelationshipType
.equals(SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF());
885 HomotypicalGroup homotypicGroup
= synonymName
.getHomotypicalGroup();
886 int hgSize
= homotypicGroup
.getTypifiedNames().size();
887 boolean isSingleInGroup
= !(hgSize
> 1);
889 if (! isSingleInGroup
){
890 boolean isHomotypicToAccepted
= synonymName
.isHomotypic(fromTaxonName
);
891 boolean hasHomotypicSynonymRelatives
= isHomotypicToAccepted ? hgSize
> 2 : hgSize
> 1;
892 if (isHomotypicToAccepted
){
893 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.";
894 String homotypicRelatives
= hasHomotypicSynonymRelatives ?
" and other synonym(s)":"";
895 message
= String
.format(message
, homotypicRelatives
);
896 throw new HomotypicalGroupChangeException(message
);
898 if (! moveHomotypicGroup
){
899 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.";
900 throw new HomotypicalGroupChangeException(message
);
903 moveHomotypicGroup
= true; //single synonym always allows to moveCompleteGroup
905 Assert
.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup
);
907 SynonymRelationship result
= null;
908 //move all synonyms to new taxon
909 List
<Synonym
> homotypicSynonyms
= fromTaxon
.getSynonymsInGroup(homotypicGroup
);
910 for (Synonym syn
: homotypicSynonyms
){
911 Set
<SynonymRelationship
> synRelations
= syn
.getSynonymRelations();
912 for (SynonymRelationship synRelation
: synRelations
){
913 if (fromTaxon
.equals(synRelation
.getAcceptedTaxon())){
914 Reference
<?
> newReference
= reference
;
915 if (newReference
== null && keepReference
){
916 newReference
= synRelation
.getCitation();
918 String newRefDetail
= referenceDetail
;
919 if (newRefDetail
== null && keepReference
){
920 newRefDetail
= synRelation
.getCitationMicroReference();
922 SynonymRelationship newSynRelation
= newTaxon
.addSynonym(syn
, newSynonymRelationshipType
, newReference
, newRefDetail
);
923 fromTaxon
.removeSynonymRelation(synRelation
, false);
925 //change homotypic group of synonym if relType is 'homotypic'
926 // if (newRelTypeIsHomotypic){
927 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
930 if (synRelation
.equals(oldSynonymRelation
)){
931 result
= newSynRelation
;
937 saveOrUpdate(newTaxon
);
938 //Assert that there is a result
940 String message
= "Old synonym relation could not be transformed into new relation. This should not happen.";
941 throw new IllegalStateException(message
);
947 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
950 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheTaxon() {
951 return dao
.getUuidAndTitleCacheTaxon();
955 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
958 public List
<UuidAndTitleCache
<TaxonBase
>> getUuidAndTitleCacheSynonym() {
959 return dao
.getUuidAndTitleCacheSynonym();