change exception type for "changeSynonymToAccepted" from IllegalState to HomotypicalC...
[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 import junit.framework.Assert;
20
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;
26
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;
66
67
68 /**
69 * @author a.kohlbecker
70 * @date 10.09.2010
71 *
72 */
73 @Service
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);
77
78 @Autowired
79 private ITaxonNameDao nameDao;
80
81 @Autowired
82 private IOrderedTermVocabularyDao orderedVocabularyDao;
83
84 @Autowired
85 private INameService nameService;
86
87 /**
88 * Constructor
89 */
90 public TaxonServiceImpl(){
91 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
92 }
93
94 /**
95 * FIXME Candidate for harmonization
96 * rename searchByName ?
97 */
98 public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
99 return dao.getTaxaByName(name, sec);
100 }
101
102 /**
103 * FIXME Candidate for harmonization
104 * list(Synonym.class, ...)
105 * (non-Javadoc)
106 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
107 */
108 public List<Synonym> getAllSynonyms(int limit, int start) {
109 return dao.getAllSynonyms(limit, start);
110 }
111
112 /**
113 * FIXME Candidate for harmonization
114 * list(Taxon.class, ...)
115 * (non-Javadoc)
116 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
117 */
118 public List<Taxon> getAllTaxa(int limit, int start) {
119 return dao.getAllTaxa(limit, start);
120 }
121
122 /**
123 * FIXME Candidate for harmonization
124 * merge with getRootTaxa(Reference sec, ..., ...)
125 * (non-Javadoc)
126 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
127 */
128 public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
129 if (cdmFetch == null){
130 cdmFetch = CdmFetch.NO_FETCH();
131 }
132 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
133 }
134
135
136 /* (non-Javadoc)
137 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
138 */
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);
141 }
142
143 /* (non-Javadoc)
144 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
145 */
146 public List<RelationshipBase> getAllRelationships(int limit, int start){
147 return dao.getAllRelationships(limit, start);
148 }
149
150 /**
151 * FIXME Candidate for harmonization
152 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
153 */
154 @Deprecated
155 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
156
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;
162 }
163
164
165
166 /*
167 * (non-Javadoc)
168 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
169 */
170 @Transactional(readOnly = false)
171 public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
172
173 TaxonNameBase<?,?> synonymName = synonym.getName();
174 synonymName.removeTaxonBase(synonym);
175 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
176 taxonName.removeTaxonBase(acceptedTaxon);
177
178 synonym.setName(taxonName);
179 acceptedTaxon.setName(synonymName);
180
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());
184 }
185
186
187 /* (non-Javadoc)
188 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
189 */
190 //TODO correct delete handling still needs to be implemented / checked
191 @Override
192 @Transactional(readOnly = false)
193 public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
194
195 TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
196 TaxonNameBase<?,?> synonymName = synonym.getName();
197 HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
198
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 HomotypicalGroupChangeException(message);
203 }
204
205 Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
206
207 SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
208 List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
209
210 for (Synonym heteroSynonym : heteroSynonyms){
211 if (synonym.equals(heteroSynonym)){
212 acceptedTaxon.removeSynonym(heteroSynonym, false);
213 }else{
214 //move synonyms in same homotypic group to new accepted taxon
215 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
216 }
217 }
218
219 //synonym.getName().removeTaxonBase(synonym);
220 //TODO correct delete handling still needs to be implemented / checked
221 if (deleteSynonym){
222 // deleteSynonym(synonym, taxon, false);
223 try {
224 this.dao.flush();
225 this.delete(synonym);
226
227 } catch (Exception e) {
228 logger.info("Can't delete old synonym from database");
229 }
230 }
231
232 return newAcceptedTaxon;
233 }
234
235
236 public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
237
238 // Get name from synonym
239 TaxonNameBase<?, ?> synonymName = synonym.getName();
240
241 // remove synonym from taxon
242 toTaxon.removeSynonym(synonym);
243
244 // Create a taxon with synonym name
245 Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
246
247 // Add taxon relation
248 fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
249
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);
253
254 return fromTaxon;
255 }
256
257
258 /* (non-Javadoc)
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)
260 */
261 @Transactional(readOnly = false)
262 @Override
263 public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
264 boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
265 // Get synonym name
266 TaxonNameBase synonymName = synonym.getName();
267 HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
268
269
270 // Switch groups
271 oldHomotypicalGroup.removeTypifiedName(synonymName);
272 newHomotypicalGroup.addTypifiedName(synonymName);
273
274 //remove existing basionym relationships
275 synonymName.removeBasionyms();
276
277 //add basionym relationship
278 if (setBasionymRelationIfApplicable){
279 Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
280 for (TaxonNameBase basionym : basionyms){
281 synonymName.addBasionym(basionym);
282 }
283 }
284
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
297
298 if (isTargetTaxon){
299 relToTargetTaxonExists = true;
300 }else{
301 if (removeFromOtherTaxa){
302 acceptedTaxon.removeSynonym(synonym, false);
303 }else{
304 //do nothing
305 }
306 }
307 }
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);
317 }
318
319 }
320
321
322 /* (non-Javadoc)
323 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
324 */
325 @Override
326 public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
327 if (clazz == null){
328 clazz = TaxonBase.class;
329 }
330 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
331 }
332
333 @Autowired
334 protected void setDao(ITaxonDao dao) {
335 this.dao = dao;
336 }
337
338 /* (non-Javadoc)
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)
340 */
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);
343
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);
347 }
348
349 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
350 }
351
352 /* (non-Javadoc)
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)
354 */
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);
357
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);
361 }
362
363 return results;
364 }
365
366 /* (non-Javadoc)
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)
368 */
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);
371
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);
375 }
376 return results;
377 }
378
379 /* (non-Javadoc)
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)
381 */
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);
384
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);
388 }
389 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
390 }
391
392 /* (non-Javadoc)
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)
394 */
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);
397
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);
401 }
402 return results;
403 }
404
405 /* (non-Javadoc)
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)
407 */
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);
410
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);
414 }
415 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
416 }
417
418 /* (non-Javadoc)
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)
420 */
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);
423
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);
427 }
428
429 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
430 }
431
432 /* (non-Javadoc)
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)
434 */
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);
437
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);
441 }
442
443 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
444 }
445
446 /* (non-Javadoc)
447 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
448 */
449 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
450 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
451 return t.getHomotypicSynonymsByHomotypicGroup();
452 }
453
454 /* (non-Javadoc)
455 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
456 */
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));
463 }
464 return heterotypicSynonymyGroups;
465 }
466
467 public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){
468
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()) {
476 clazz = Taxon.class;
477 //propertyPath = configurator.getTaxonPropertyPath();
478 } else if (configurator.isDoSynonyms()) {
479 clazz = Synonym.class;
480 //propertyPath = configurator.getSynonymPropertyPath();
481 }
482
483
484 result = dao.getTaxaByNameForEditor(clazz, configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
485 return result;
486 }
487
488 /* (non-Javadoc)
489 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
490 */
491 public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {
492
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;
496
497 // Taxa and synonyms
498 long numberTaxaResults = 0L;
499
500 Class<? extends TaxonBase> clazz = null;
501 List<String> propertyPath = new ArrayList<String>();
502 if(configurator.getTaxonPropertyPath() != null){
503 propertyPath.addAll(configurator.getTaxonPropertyPath());
504 }
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()) {
510 clazz = Taxon.class;
511 //propertyPath = configurator.getTaxonPropertyPath();
512 } else if (configurator.isDoSynonyms()) {
513 clazz = Synonym.class;
514 //propertyPath = configurator.getSynonymPropertyPath();
515 }
516
517 if(clazz != null){
518 if(configurator.getPageSize() != null){ // no point counting if we need all anyway
519 numberTaxaResults =
520 dao.countTaxaByName(clazz,
521 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
522 configurator.getNamedAreas());
523 }
524
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);
530 }
531 }
532
533 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
534
535 if(taxa != null){
536 results.addAll(taxa);
537 }
538
539 numberOfResults += numberTaxaResults;
540
541 // Names without taxa
542 if (configurator.isDoNamesWithoutTaxa()) {
543 int numberNameResults = 0;
544
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);
553 numberNameResults++;
554 }
555 }
556 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
557 numberOfResults += numberNameResults;
558 }
559 }
560
561 // Taxa from common names
562
563 if (configurator.isDoTaxaByCommonNames()) {
564 taxa = null;
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());
568 }
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());
571 }
572 if(taxa != null){
573 results.addAll(taxa);
574 }
575 numberOfResults += numberTaxaResults;
576
577 }
578
579 return new DefaultPagerImpl<IdentifiableEntity>
580 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
581 }
582
583 public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
584 return dao.getUuidAndTitleCache();
585 }
586
587 /* (non-Javadoc)
588 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
589 */
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()){
598
599 //find the best matching representation
600 medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
601
602 }
603 }
604 }
605 return medRep;
606 }
607
608 /* (non-Javadoc)
609 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
610 */
611 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
612 return this.dao.findById(listOfIDs);
613 }
614
615 /* (non-Javadoc)
616 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
617 */
618 public int countAllRelationships() {
619 return this.dao.countAllRelationships();
620 }
621
622 /* (non-Javadoc)
623 * @see eu.etaxonomy.cdm.api.service.ITaxonService#createAllInferredSynonyms(eu.etaxonomy.cdm.model.taxon.Classification, eu.etaxonomy.cdm.model.taxon.Taxon)
624 */
625 public List<Synonym> createAllInferredSynonyms(Classification tree,
626 Taxon taxon) {
627
628 return this.dao.createAllInferredSynonyms(taxon, tree);
629 }
630
631 /* (non-Javadoc)
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)
633 */
634 public List<Synonym> createInferredSynonyms(Classification tree, Taxon taxon, SynonymRelationshipType type) {
635 return this.dao.createInferredSynonyms(taxon, tree, type);
636 }
637
638 /* (non-Javadoc)
639 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
640 */
641 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
642 return this.dao.findIdenticalTaxonNames(propertyPath);
643 }
644
645
646 /* (non-Javadoc)
647 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
648 */
649 @Transactional(readOnly = false)
650 @Override
651 public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
652 if (synonym == null){
653 return;
654 }
655 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
656
657 //remove synonymRelationship
658 Set<Taxon> taxonSet = new HashSet<Taxon>();
659 if (taxon != null){
660 taxonSet.add(taxon);
661 }else{
662 taxonSet.addAll(synonym.getAcceptedTaxa());
663 }
664 for (Taxon relatedTaxon : taxonSet){
665 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
666 relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
667 }
668 this.saveOrUpdate(synonym);
669
670 //TODO remove name from homotypical group?
671
672 //remove synonym (if necessary)
673 if (synonym.getSynonymRelations().isEmpty()){
674 TaxonNameBase<?,?> name = synonym.getName();
675 synonym.setName(null);
676 dao.delete(synonym);
677
678 //remove name if possible (and required)
679 if (name != null && removeNameIfPossible){
680 try{
681 nameService.delete(name, new NameDeletionConfigurator());
682 }catch (DataChangeNoRollbackException ex){
683 if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");
684 }
685 }
686 }
687 }
688
689
690 /* (non-Javadoc)
691 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
692 */
693 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
694
695 return this.dao.findIdenticalNamesNew(propertyPath);
696 }
697
698 /* (non-Javadoc)
699 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
700 */
701 public String getPhylumName(TaxonNameBase name){
702 return this.dao.getPhylumName(name);
703 }
704
705 /* (non-Javadoc)
706 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
707 */
708 public long deleteSynonymRelationships(Synonym syn) {
709 return dao.deleteSynonymRelationships(syn, null);
710 }
711
712
713 /* (non-Javadoc)
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)
715 */
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);
720
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);
724 }
725 return results;
726 }
727
728 /* (non-Javadoc)
729 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
730 */
731 @Override
732 public Taxon findBestMatchingTaxon(String taxonName) {
733 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
734 config.setTaxonNameTitle(taxonName);
735 return findBestMatchingTaxon(config);
736 }
737
738
739
740 @Override
741 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
742
743 Taxon bestCandidate = null;
744 try{
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() ){
755 continue;
756 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
757 bestCandidate = newCanditate;
758 countEqualCandidates = 1;
759 bestCandidateMatchesSecUuid = true;
760 continue;
761 }
762
763 boolean newCandidateInClassification = isInClassification(newCanditate, config);
764 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
765 continue;
766 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
767 bestCandidate = newCanditate;
768 countEqualCandidates = 1;
769 bestCandidateIsInClassification = true;
770 continue;
771 }
772 if (bestCandidate == null){
773 bestCandidate = newCanditate;
774 countEqualCandidates = 1;
775 continue;
776 }
777
778 }else{ //not Taxon.class
779 continue;
780 }
781 countEqualCandidates++;
782
783 }
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;
788 } else {
789 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
790 return bestCandidate;
791 }
792 }
793
794
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;
807 } else {
808 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
809 return bestCandidate;
810 }
811 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
812 }
813 }
814 }
815 }
816
817 } catch (Exception e){
818 logger.error(e);
819 }
820
821 return bestCandidate;
822 }
823
824 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
825 UUID configClassificationUuid = config.getClassificationUuid();
826 if (configClassificationUuid == null){
827 return false;
828 }
829 for (TaxonNode node : taxon.getTaxonNodes()){
830 UUID classUuid = node.getClassification().getUuid();
831 if (configClassificationUuid.equals(classUuid)){
832 return true;
833 }
834 }
835 return false;
836 }
837
838 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
839 UUID configSecUuid = config.getSecUuid();
840 if (configSecUuid == null){
841 return false;
842 }
843 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
844 return configSecUuid.equals(taxonSecUuid);
845 }
846
847 /* (non-Javadoc)
848 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
849 */
850 @Override
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() );
857 return result;
858 } else {
859 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
860 return result;
861 }
862 }
863 return null;
864 }
865
866
867 /* (non-Javadoc)
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)
869 */
870 @Override
871 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
872 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
873
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();
882 }
883 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
884
885 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
886 int hgSize = homotypicGroup.getTypifiedNames().size();
887 boolean isSingleInGroup = !(hgSize > 1);
888
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);
897 }
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);
901 }
902 }else{
903 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
904 }
905 Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
906
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();
917 }
918 String newRefDetail = referenceDetail;
919 if (newRefDetail == null && keepReference){
920 newRefDetail = synRelation.getCitationMicroReference();
921 }
922 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
923 fromTaxon.removeSynonymRelation(synRelation, false);
924 //
925 //change homotypic group of synonym if relType is 'homotypic'
926 // if (newRelTypeIsHomotypic){
927 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
928 // }
929 //set result
930 if (synRelation.equals(oldSynonymRelation)){
931 result = newSynRelation;
932 }
933 }
934 }
935
936 }
937 saveOrUpdate(newTaxon);
938 //Assert that there is a result
939 if (result == null){
940 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
941 throw new IllegalStateException(message);
942 }
943 return result;
944 }
945
946 /* (non-Javadoc)
947 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
948 */
949 @Override
950 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
951 return dao.getUuidAndTitleCacheTaxon();
952 }
953
954 /* (non-Javadoc)
955 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
956 */
957 @Override
958 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
959 return dao.getUuidAndTitleCacheSynonym();
960 }
961
962 }