remove missing import for junit.framework.assert
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
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.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.UUID;
18
19
20 import org.apache.log4j.Logger;
21 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.stereotype.Service;
23 import org.springframework.transaction.annotation.Propagation;
24 import org.springframework.transaction.annotation.Transactional;
25
26 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
27 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
28 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
29 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
30 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
31 import eu.etaxonomy.cdm.api.service.pager.Pager;
32 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
33 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
34 import eu.etaxonomy.cdm.model.common.CdmBase;
35 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
36 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
37 import eu.etaxonomy.cdm.model.common.RelationshipBase;
38 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
39 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
40 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
41 import eu.etaxonomy.cdm.model.description.TaxonDescription;
42 import eu.etaxonomy.cdm.model.media.Media;
43 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
44 import eu.etaxonomy.cdm.model.media.MediaUtils;
45 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
46 import eu.etaxonomy.cdm.model.name.Rank;
47 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
48 import eu.etaxonomy.cdm.model.reference.Reference;
49 import eu.etaxonomy.cdm.model.taxon.Classification;
50 import eu.etaxonomy.cdm.model.taxon.Synonym;
51 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
52 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
53 import eu.etaxonomy.cdm.model.taxon.Taxon;
54 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
55 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
56 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
57 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
58 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
59 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
60 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
61 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
62 import eu.etaxonomy.cdm.persistence.query.MatchMode;
63 import eu.etaxonomy.cdm.persistence.query.OrderHint;
64 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
65
66
67 /**
68 * @author a.kohlbecker
69 * @date 10.09.2010
70 *
71 */
72 @Service
73 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
74 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
75 private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
76
77 @Autowired
78 private ITaxonNameDao nameDao;
79
80 @Autowired
81 private IOrderedTermVocabularyDao orderedVocabularyDao;
82
83 @Autowired
84 private INameService nameService;
85
86 /**
87 * Constructor
88 */
89 public TaxonServiceImpl(){
90 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
91 }
92
93 /**
94 * FIXME Candidate for harmonization
95 * rename searchByName ?
96 */
97 public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
98 return dao.getTaxaByName(name, sec);
99 }
100
101 /**
102 * FIXME Candidate for harmonization
103 * list(Synonym.class, ...)
104 * (non-Javadoc)
105 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
106 */
107 public List<Synonym> getAllSynonyms(int limit, int start) {
108 return dao.getAllSynonyms(limit, start);
109 }
110
111 /**
112 * FIXME Candidate for harmonization
113 * list(Taxon.class, ...)
114 * (non-Javadoc)
115 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
116 */
117 public List<Taxon> getAllTaxa(int limit, int start) {
118 return dao.getAllTaxa(limit, start);
119 }
120
121 /**
122 * FIXME Candidate for harmonization
123 * merge with getRootTaxa(Reference sec, ..., ...)
124 * (non-Javadoc)
125 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
126 */
127 public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
128 if (cdmFetch == null){
129 cdmFetch = CdmFetch.NO_FETCH();
130 }
131 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
132 }
133
134
135 /* (non-Javadoc)
136 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
137 */
138 public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
139 return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
140 }
141
142 /* (non-Javadoc)
143 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
144 */
145 public List<RelationshipBase> getAllRelationships(int limit, int start){
146 return dao.getAllRelationships(limit, start);
147 }
148
149 /**
150 * FIXME Candidate for harmonization
151 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
152 */
153 @Deprecated
154 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
155
156 String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
157 UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
158 OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
159 (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
160 return taxonRelTypeVocabulary;
161 }
162
163
164
165 /*
166 * (non-Javadoc)
167 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
168 */
169 @Transactional(readOnly = false)
170 public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
171
172 TaxonNameBase<?,?> synonymName = synonym.getName();
173 synonymName.removeTaxonBase(synonym);
174 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
175 taxonName.removeTaxonBase(acceptedTaxon);
176
177 synonym.setName(taxonName);
178 acceptedTaxon.setName(synonymName);
179
180 // the accepted taxon needs a new uuid because the concept has changed
181 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
182 //acceptedTaxon.setUuid(UUID.randomUUID());
183 }
184
185
186 /* (non-Javadoc)
187 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
188 */
189 //TODO correct delete handling still needs to be implemented / checked
190 @Override
191 @Transactional(readOnly = false)
192 public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
193
194 TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
195 TaxonNameBase<?,?> synonymName = synonym.getName();
196 HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
197
198 //check synonym is not homotypic
199 if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
200 String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
201 throw new HomotypicalGroupChangeException(message);
202 }
203
204 Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
205
206 SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
207 List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
208
209 for (Synonym heteroSynonym : heteroSynonyms){
210 if (synonym.equals(heteroSynonym)){
211 acceptedTaxon.removeSynonym(heteroSynonym, false);
212 }else{
213 //move synonyms in same homotypic group to new accepted taxon
214 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
215 }
216 }
217
218 //synonym.getName().removeTaxonBase(synonym);
219 //TODO correct delete handling still needs to be implemented / checked
220 if (deleteSynonym){
221 // deleteSynonym(synonym, taxon, false);
222 try {
223 this.dao.flush();
224 this.delete(synonym);
225
226 } catch (Exception e) {
227 logger.info("Can't delete old synonym from database");
228 }
229 }
230
231 return newAcceptedTaxon;
232 }
233
234
235 public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
236
237 // Get name from synonym
238 TaxonNameBase<?, ?> synonymName = synonym.getName();
239
240 // remove synonym from taxon
241 toTaxon.removeSynonym(synonym);
242
243 // Create a taxon with synonym name
244 Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
245
246 // Add taxon relation
247 fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
248
249 // since we are swapping names, we have to detach the name from the synonym completely.
250 // Otherwise the synonym will still be in the list of typified names.
251 synonym.getName().removeTaxonBase(synonym);
252
253 return fromTaxon;
254 }
255
256
257 /* (non-Javadoc)
258 * @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)
259 */
260 @Transactional(readOnly = false)
261 @Override
262 public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
263 boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
264 // Get synonym name
265 TaxonNameBase synonymName = synonym.getName();
266 HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
267
268
269 // Switch groups
270 oldHomotypicalGroup.removeTypifiedName(synonymName);
271 newHomotypicalGroup.addTypifiedName(synonymName);
272
273 //remove existing basionym relationships
274 synonymName.removeBasionyms();
275
276 //add basionym relationship
277 if (setBasionymRelationIfApplicable){
278 Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
279 for (TaxonNameBase basionym : basionyms){
280 synonymName.addBasionym(basionym);
281 }
282 }
283
284 //set synonym relationship correctly
285 // SynonymRelationship relToTaxon = null;
286 boolean relToTargetTaxonExists = false;
287 Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
288 for (SynonymRelationship rel : existingRelations){
289 Taxon acceptedTaxon = rel.getAcceptedTaxon();
290 boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
291 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
292 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
293 SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
294 rel.setType(newRelationType);
295 //TODO handle citation and microCitation
296
297 if (isTargetTaxon){
298 relToTargetTaxonExists = true;
299 }else{
300 if (removeFromOtherTaxa){
301 acceptedTaxon.removeSynonym(synonym, false);
302 }else{
303 //do nothing
304 }
305 }
306 }
307 if (targetTaxon != null && ! relToTargetTaxonExists ){
308 Taxon acceptedTaxon = targetTaxon;
309 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
310 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
311 SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
312 //TODO handle citation and microCitation
313 Reference citation = null;
314 String microCitation = null;
315 acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
316 }
317
318 }
319
320
321 /* (non-Javadoc)
322 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
323 */
324 @Override
325 public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
326 if (clazz == null){
327 clazz = TaxonBase.class;
328 }
329 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
330 }
331
332 @Autowired
333 protected void setDao(ITaxonDao dao) {
334 this.dao = dao;
335 }
336
337 /* (non-Javadoc)
338 * @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)
339 */
340 public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
341 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
342
343 List<TaxonBase> results = new ArrayList<TaxonBase>();
344 if(numberOfResults > 0) { // no point checking again
345 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
346 }
347
348 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
349 }
350
351 /* (non-Javadoc)
352 * @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)
353 */
354 public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
355 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
356
357 List<TaxonBase> results = new ArrayList<TaxonBase>();
358 if(numberOfResults > 0) { // no point checking again
359 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
360 }
361
362 return results;
363 }
364
365 /* (non-Javadoc)
366 * @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)
367 */
368 public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
369 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
370
371 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
372 if(numberOfResults > 0) { // no point checking again
373 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
374 }
375 return results;
376 }
377
378 /* (non-Javadoc)
379 * @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)
380 */
381 public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
382 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
383
384 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
385 if(numberOfResults > 0) { // no point checking again
386 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
387 }
388 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
389 }
390
391 /* (non-Javadoc)
392 * @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)
393 */
394 public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
395 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
396
397 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
398 if(numberOfResults > 0) { // no point checking again
399 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
400 }
401 return results;
402 }
403
404 /* (non-Javadoc)
405 * @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)
406 */
407 public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
408 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
409
410 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
411 if(numberOfResults > 0) { // no point checking again
412 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
413 }
414 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
415 }
416
417 /* (non-Javadoc)
418 * @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)
419 */
420 public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
421 Integer numberOfResults = dao.countSynonyms(taxon, type);
422
423 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
424 if(numberOfResults > 0) { // no point checking again
425 results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
426 }
427
428 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
429 }
430
431 /* (non-Javadoc)
432 * @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)
433 */
434 public Pager<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
435 Integer numberOfResults = dao.countSynonyms(synonym, type);
436
437 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
438 if(numberOfResults > 0) { // no point checking again
439 results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
440 }
441
442 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
443 }
444
445 /* (non-Javadoc)
446 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
447 */
448 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
449 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
450 return t.getHomotypicSynonymsByHomotypicGroup();
451 }
452
453 /* (non-Javadoc)
454 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
455 */
456 public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
457 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
458 List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
459 List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
460 for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
461 heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
462 }
463 return heterotypicSynonymyGroups;
464 }
465
466 public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){
467
468 List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();
469 Class<? extends TaxonBase> clazz = null;
470 if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
471 clazz = TaxonBase.class;
472 //propertyPath.addAll(configurator.getTaxonPropertyPath());
473 //propertyPath.addAll(configurator.getSynonymPropertyPath());
474 } else if(configurator.isDoTaxa()) {
475 clazz = Taxon.class;
476 //propertyPath = configurator.getTaxonPropertyPath();
477 } else if (configurator.isDoSynonyms()) {
478 clazz = Synonym.class;
479 //propertyPath = configurator.getSynonymPropertyPath();
480 }
481
482
483 result = dao.getTaxaByNameForEditor(clazz, configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
484 return result;
485 }
486
487 /* (non-Javadoc)
488 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
489 */
490 public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {
491
492 List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
493 int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
494 List<TaxonBase> taxa = null;
495
496 // Taxa and synonyms
497 long numberTaxaResults = 0L;
498
499 Class<? extends TaxonBase> clazz = null;
500 List<String> propertyPath = new ArrayList<String>();
501 if(configurator.getTaxonPropertyPath() != null){
502 propertyPath.addAll(configurator.getTaxonPropertyPath());
503 }
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();
514 }
515
516 if(clazz != null){
517 if(configurator.getPageSize() != null){ // no point counting if we need all anyway
518 numberTaxaResults =
519 dao.countTaxaByName(clazz,
520 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
521 configurator.getNamedAreas());
522 }
523
524 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
525 taxa = dao.getTaxaByName(clazz,
526 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
527 configurator.getNamedAreas(), configurator.getPageSize(),
528 configurator.getPageNumber(), propertyPath);
529 }
530 }
531
532 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
533
534 if(taxa != null){
535 results.addAll(taxa);
536 }
537
538 numberOfResults += numberTaxaResults;
539
540 // Names without taxa
541 if (configurator.isDoNamesWithoutTaxa()) {
542 int numberNameResults = 0;
543
544 List<? extends TaxonNameBase<?,?>> names =
545 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
546 configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
547 if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
548 if (names.size() > 0) {
549 for (TaxonNameBase<?,?> taxonName : names) {
550 if (taxonName.getTaxonBases().size() == 0) {
551 results.add(taxonName);
552 numberNameResults++;
553 }
554 }
555 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
556 numberOfResults += numberNameResults;
557 }
558 }
559
560 // Taxa from common names
561
562 if (configurator.isDoTaxaByCommonNames()) {
563 taxa = null;
564 numberTaxaResults = 0;
565 if(configurator.getPageSize() != null){// no point counting if we need all anyway
566 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
567 }
568 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
569 taxa = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
570 }
571 if(taxa != null){
572 results.addAll(taxa);
573 }
574 numberOfResults += numberTaxaResults;
575
576 }
577
578 return new DefaultPagerImpl<IdentifiableEntity>
579 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
580 }
581
582 public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
583 return dao.getUuidAndTitleCache();
584 }
585
586 /* (non-Javadoc)
587 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
588 */
589 public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
590 List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
591 taxon = (Taxon)dao.load(taxon.getUuid());
592 Set<TaxonDescription> descriptions = taxon.getDescriptions();
593 for (TaxonDescription taxDesc: descriptions){
594 Set<DescriptionElementBase> elements = taxDesc.getElements();
595 for (DescriptionElementBase descElem: elements){
596 for(Media media : descElem.getMedia()){
597
598 //find the best matching representation
599 medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
600
601 }
602 }
603 }
604 return medRep;
605 }
606
607 /* (non-Javadoc)
608 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
609 */
610 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
611 return this.dao.findById(listOfIDs);
612 }
613
614 /* (non-Javadoc)
615 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
616 */
617 public int countAllRelationships() {
618 return this.dao.countAllRelationships();
619 }
620
621 /* (non-Javadoc)
622 * @see eu.etaxonomy.cdm.api.service.ITaxonService#createAllInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon)
623 */
624 public List<Synonym> createAllInferredSynonyms(Classification tree,
625 Taxon taxon) {
626
627 return this.dao.createAllInferredSynonyms(taxon, tree);
628 }
629
630 /* (non-Javadoc)
631 * @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)
632 */
633 public List<Synonym> createInferredSynonyms(Classification tree, Taxon taxon, SynonymRelationshipType type) {
634 return this.dao.createInferredSynonyms(taxon, tree, type);
635 }
636
637 /* (non-Javadoc)
638 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
639 */
640 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
641 return this.dao.findIdenticalTaxonNames(propertyPath);
642 }
643
644
645 /* (non-Javadoc)
646 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
647 */
648 @Transactional(readOnly = false)
649 @Override
650 public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
651 if (synonym == null){
652 return;
653 }
654 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
655
656 //remove synonymRelationship
657 Set<Taxon> taxonSet = new HashSet<Taxon>();
658 if (taxon != null){
659 taxonSet.add(taxon);
660 }else{
661 taxonSet.addAll(synonym.getAcceptedTaxa());
662 }
663 for (Taxon relatedTaxon : taxonSet){
664 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
665 relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
666 }
667 this.saveOrUpdate(synonym);
668
669 //TODO remove name from homotypical group?
670
671 //remove synonym (if necessary)
672 if (synonym.getSynonymRelations().isEmpty()){
673 TaxonNameBase<?,?> name = synonym.getName();
674 synonym.setName(null);
675 dao.delete(synonym);
676
677 //remove name if possible (and required)
678 if (name != null && removeNameIfPossible){
679 try{
680 nameService.delete(name, new NameDeletionConfigurator());
681 }catch (DataChangeNoRollbackException ex){
682 if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");
683 }
684 }
685 }
686 }
687
688
689 /* (non-Javadoc)
690 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
691 */
692 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
693
694 return this.dao.findIdenticalNamesNew(propertyPath);
695 }
696
697 /* (non-Javadoc)
698 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
699 */
700 public String getPhylumName(TaxonNameBase name){
701 return this.dao.getPhylumName(name);
702 }
703
704 /* (non-Javadoc)
705 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
706 */
707 public long deleteSynonymRelationships(Synonym syn) {
708 return dao.deleteSynonymRelationships(syn, null);
709 }
710
711
712 /* (non-Javadoc)
713 * @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)
714 */
715 public List<SynonymRelationship> listSynonymRelationships(
716 TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
717 List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
718 Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
719
720 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
721 if(numberOfResults > 0) { // no point checking again
722 results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
723 }
724 return results;
725 }
726
727 /* (non-Javadoc)
728 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
729 */
730 @Override
731 public Taxon findBestMatchingTaxon(String taxonName) {
732 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
733 config.setTaxonNameTitle(taxonName);
734 return findBestMatchingTaxon(config);
735 }
736
737
738
739 @Override
740 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
741
742 Taxon bestCandidate = null;
743 try{
744 // 1. search for acceptet taxa
745 List<TaxonBase> taxonList = dao.findByNameTitleCache(Taxon.class, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
746 boolean bestCandidateMatchesSecUuid = false;
747 boolean bestCandidateIsInClassification = false;
748 int countEqualCandidates = 0;
749 for(TaxonBase taxonBaseCandidate : taxonList){
750 if(taxonBaseCandidate instanceof Taxon){
751 Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
752 boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
753 if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
754 continue;
755 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
756 bestCandidate = newCanditate;
757 countEqualCandidates = 1;
758 bestCandidateMatchesSecUuid = true;
759 continue;
760 }
761
762 boolean newCandidateInClassification = isInClassification(newCanditate, config);
763 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
764 continue;
765 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
766 bestCandidate = newCanditate;
767 countEqualCandidates = 1;
768 bestCandidateIsInClassification = true;
769 continue;
770 }
771 if (bestCandidate == null){
772 bestCandidate = newCanditate;
773 countEqualCandidates = 1;
774 continue;
775 }
776
777 }else{ //not Taxon.class
778 continue;
779 }
780 countEqualCandidates++;
781
782 }
783 if (bestCandidate != null){
784 if(countEqualCandidates > 1){
785 logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
786 return bestCandidate;
787 } else {
788 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
789 return bestCandidate;
790 }
791 }
792
793
794 // 2. search for synonyms
795 if (config.isIncludeSynonyms()){
796 List<TaxonBase> synonymList = dao.findByNameTitleCache(Synonym.class, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
797 for(TaxonBase taxonBase : synonymList){
798 if(taxonBase instanceof Synonym){
799 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
800 Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
801 if(!acceptetdCandidates.isEmpty()){
802 bestCandidate = acceptetdCandidates.iterator().next();
803 if(acceptetdCandidates.size() == 1){
804 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
805 return bestCandidate;
806 } else {
807 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
808 return bestCandidate;
809 }
810 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
811 }
812 }
813 }
814 }
815
816 } catch (Exception e){
817 logger.error(e);
818 }
819
820 return bestCandidate;
821 }
822
823 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
824 UUID configClassificationUuid = config.getClassificationUuid();
825 if (configClassificationUuid == null){
826 return false;
827 }
828 for (TaxonNode node : taxon.getTaxonNodes()){
829 UUID classUuid = node.getClassification().getUuid();
830 if (configClassificationUuid.equals(classUuid)){
831 return true;
832 }
833 }
834 return false;
835 }
836
837 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
838 UUID configSecUuid = config.getSecUuid();
839 if (configSecUuid == null){
840 return false;
841 }
842 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
843 return configSecUuid.equals(taxonSecUuid);
844 }
845
846 /* (non-Javadoc)
847 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
848 */
849 @Override
850 public Synonym findBestMatchingSynonym(String taxonName) {
851 List<TaxonBase> synonymList = dao.findByNameTitleCache(Synonym.class, taxonName, null, MatchMode.EXACT, null, 0, null, null);
852 if(! synonymList.isEmpty()){
853 Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
854 if(synonymList.size() == 1){
855 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
856 return result;
857 } else {
858 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
859 return result;
860 }
861 }
862 return null;
863 }
864
865
866 /* (non-Javadoc)
867 * @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)
868 */
869 @Override
870 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
871 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
872
873 Synonym synonym = oldSynonymRelation.getSynonym();
874 Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
875 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
876 TaxonNameBase<?,?> synonymName = synonym.getName();
877 TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
878 //set default relationship type
879 if (newSynonymRelationshipType == null){
880 newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
881 }
882 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
883
884 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
885 int hgSize = homotypicGroup.getTypifiedNames().size();
886 boolean isSingleInGroup = !(hgSize > 1);
887
888 if (! isSingleInGroup){
889 boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
890 boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
891 if (isHomotypicToAccepted){
892 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.";
893 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
894 message = String.format(message, homotypicRelatives);
895 throw new HomotypicalGroupChangeException(message);
896 }
897 if (! moveHomotypicGroup){
898 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.";
899 throw new HomotypicalGroupChangeException(message);
900 }
901 }else{
902 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
903 }
904 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
905
906 SynonymRelationship result = null;
907 //move all synonyms to new taxon
908 List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
909 for (Synonym syn: homotypicSynonyms){
910 Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
911 for (SynonymRelationship synRelation : synRelations){
912 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
913 Reference<?> newReference = reference;
914 if (newReference == null && keepReference){
915 newReference = synRelation.getCitation();
916 }
917 String newRefDetail = referenceDetail;
918 if (newRefDetail == null && keepReference){
919 newRefDetail = synRelation.getCitationMicroReference();
920 }
921 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
922 fromTaxon.removeSynonymRelation(synRelation, false);
923 //
924 //change homotypic group of synonym if relType is 'homotypic'
925 // if (newRelTypeIsHomotypic){
926 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
927 // }
928 //set result
929 if (synRelation.equals(oldSynonymRelation)){
930 result = newSynRelation;
931 }
932 }
933 }
934
935 }
936 saveOrUpdate(newTaxon);
937 //Assert that there is a result
938 if (result == null){
939 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
940 throw new IllegalStateException(message);
941 }
942 return result;
943 }
944
945 /* (non-Javadoc)
946 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
947 */
948 @Override
949 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
950 return dao.getUuidAndTitleCacheTaxon();
951 }
952
953 /* (non-Javadoc)
954 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
955 */
956 @Override
957 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
958 return dao.getUuidAndTitleCacheSynonym();
959 }
960
961 }