TaxonomicTree implementation (service)
[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.Collection;
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.UUID;
21
22 import org.apache.log4j.Logger;
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.stereotype.Service;
25 import org.springframework.transaction.TransactionStatus;
26 import org.springframework.transaction.annotation.Transactional;
27
28 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
29 import eu.etaxonomy.cdm.api.service.pager.Pager;
30 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
31 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
32 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
33 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
34 import eu.etaxonomy.cdm.model.common.RelationshipBase;
35 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
36 import eu.etaxonomy.cdm.model.description.DescriptionBase;
37 import eu.etaxonomy.cdm.model.description.TaxonDescription;
38 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
39 import eu.etaxonomy.cdm.model.name.Rank;
40 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
41 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
42 import eu.etaxonomy.cdm.model.taxon.Synonym;
43 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
44 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
45 import eu.etaxonomy.cdm.model.taxon.Taxon;
46 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
47 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
48 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
49 import eu.etaxonomy.cdm.model.taxon.TaxonomicTree;
50 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
51 import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;
52 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
53 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
54 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonomicTreeDao;
55 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
56 import eu.etaxonomy.cdm.persistence.query.OrderHint;
57 import eu.etaxonomy.cdm.persistence.query.SelectMode;
58
59
60 @Service
61 @Transactional(readOnly = true)
62 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService {
63 private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
64
65 @Autowired
66 private ITaxonNameDao nameDao;
67 @Autowired
68 private ITaxonomicTreeDao taxonTreeDao;
69 // @Autowired
70 // @Qualifier("nonViralNameDaoHibernateImpl")
71 // private INonViralNameDao nonViralNameDao;
72 @Autowired
73 private IOrderedTermVocabularyDao orderedVocabularyDao;
74 @Autowired
75 private IDescriptionDao descriptionDao;
76
77
78 /**
79 * Constructor
80 */
81 public TaxonServiceImpl(){
82 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
83 }
84
85 /* (non-Javadoc)
86 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getTaxonByUuid(java.util.UUID)
87 */
88 public TaxonBase getTaxonByUuid(UUID uuid) {
89 return super.findByUuid(uuid);
90 }
91
92 /* (non-Javadoc)
93 * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)
94 */
95 @Transactional(readOnly = false)
96 public UUID saveTaxon(TaxonBase taxon) {
97 return super.saveCdmObject(taxon);
98 }
99
100 //@Transactional(readOnly = false)
101 public UUID saveTaxon(TaxonBase taxon, TransactionStatus txStatus) {
102 //return super.saveCdmObject(taxon, txStatus);
103 return super.saveCdmObject(taxon);
104 }
105
106
107 /* (non-Javadoc)
108 * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxonAll(java.util.Collection)
109 */
110 @Transactional(readOnly = false)
111 public Map<UUID, ? extends TaxonBase> saveTaxonAll(Collection<? extends TaxonBase> taxonCollection){
112 return saveCdmObjectAll(taxonCollection);
113 }
114
115 /* (non-Javadoc)
116 * @see eu.etaxonomy.cdm.api.service.ITaxonService#removeTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)
117 */
118 @Transactional(readOnly = false)
119 public UUID removeTaxon(TaxonBase taxon) {
120 return super.removeCdmObject(taxon);
121 }
122
123 public List<TaxonBase> searchTaxaByName(String name, ReferenceBase sec) {
124 return dao.getTaxaByName(name, sec);
125 }
126
127 /* (non-Javadoc)
128 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxonBases(int, int)
129 */
130 public List<TaxonBase> getAllTaxonBases(int limit, int start){
131 return dao.list(limit, start);
132 }
133
134 /* (non-Javadoc)
135 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
136 */
137 public List<Taxon> getAllTaxa(int limit, int start){
138 return dao.getAllTaxa(limit, start);
139 }
140
141 /* (non-Javadoc)
142 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
143 */
144 public List<Synonym> getAllSynonyms(int limit, int start) {
145 return dao.getAllSynonyms(limit, start);
146 }
147
148
149 /* (non-Javadoc)
150 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxonomicTrees(int, int)
151 */
152 public List<TaxonomicTree> getAllTaxonomicTrees(int limit, int start) {
153 return taxonTreeDao.list(limit, start);
154 }
155
156 /* (non-Javadoc)
157 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getTaxonomicTreeByUuid(java.util.UUID)
158 */
159 public TaxonomicTree getTaxonomicTreeByUuid(UUID uuid){
160 return taxonTreeDao.findByUuid(uuid);
161 }
162
163 /* (non-Javadoc)
164 * @see eu.etaxonomy.cdm.api.service.ITaxonService#saveTaxonomicTree(eu.etaxonomy.cdm.model.taxon.TaxonomicTree)
165 */
166 @Transactional(readOnly = false)
167 public UUID saveTaxonomicTree(TaxonomicTree tree){
168 return taxonTreeDao.saveOrUpdate(tree);
169 }
170
171 /* (non-Javadoc)
172 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase)
173 */
174 public List<Taxon> getRootTaxa(ReferenceBase sec){
175 return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true);
176 }
177
178 /* (non-Javadoc)
179 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean)
180 */
181 public List<Taxon> getRootTaxa(ReferenceBase sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
182 if (cdmFetch == null){
183 cdmFetch = CdmFetch.NO_FETCH();
184 }
185 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
186 }
187
188 /* (non-Javadoc)
189 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)
190 */
191 public List<Taxon> getRootTaxa(ReferenceBase sec, boolean onlyWithChildren,
192 boolean withMisapplications) {
193 return dao.getRootTaxa(sec, null, onlyWithChildren, withMisapplications);
194 }
195
196 /* (non-Javadoc)
197 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.ReferenceBase, boolean, boolean)
198 */
199 public List<Taxon> getRootTaxa(Rank rank, ReferenceBase sec, boolean onlyWithChildren,
200 boolean withMisapplications, List<String> propertyPaths) {
201 return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
202 }
203
204 public List<RelationshipBase> getAllRelationships(int limit, int start){
205 return dao.getAllRelationships(limit, start);
206 }
207
208 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
209
210 String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
211 UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
212 OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
213 (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
214 return taxonRelTypeVocabulary;
215 }
216
217 /* (non-Javadoc)
218 * @see eu.etaxonomy.cdm.api.service.ITaxonService#makeTaxonSynonym(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.Taxon)
219 */
220 @Transactional(readOnly = false)
221 public Synonym makeTaxonSynonym(Taxon oldTaxon, Taxon newAcceptedTaxon, SynonymRelationshipType synonymType, ReferenceBase citation, String citationMicroReference) {
222 if (oldTaxon == null || newAcceptedTaxon == null || oldTaxon.getName() == null){
223 return null;
224 }
225
226 // Move oldTaxon to newTaxon
227 TaxonNameBase<?,?> synonymName = oldTaxon.getName();
228 if (synonymType == null){
229 if (synonymName.isHomotypic(newAcceptedTaxon.getName())){
230 synonymType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
231 }else{
232 //TODO synonymType
233 synonymType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
234 }
235 }
236 SynonymRelationship synRel = newAcceptedTaxon.addSynonymName(synonymName, synonymType, citation, citationMicroReference);
237
238 //Move Synonym Relations to new Taxon
239 for(SynonymRelationship synRelation : oldTaxon.getSynonymRelations()){
240 newAcceptedTaxon.addSynonym(synRelation.getSynonym(), synRelation.getType(),
241 synRelation.getCitation(), synRelation.getCitationMicroReference());
242 }
243
244 //Move Taxon RelationShips to new Taxon
245 Set<TaxonRelationship> removableTaxonRels = new HashSet<TaxonRelationship>();
246 for(TaxonRelationship taxonRelation : oldTaxon.getTaxonRelations()){
247 //CHILDREN
248 if (taxonRelation.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())){
249 if (taxonRelation.getFromTaxon() == oldTaxon){
250 removableTaxonRels.add(taxonRelation);
251 // oldTaxon.removeTaxonRelation(taxonRelation);
252 }else if(taxonRelation.getToTaxon() == oldTaxon){
253 newAcceptedTaxon.addTaxonomicChild(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());
254 removableTaxonRels.add(taxonRelation);
255 // oldTaxon.removeTaxonRelation(taxonRelation);
256 }else{
257 logger.warn("Taxon is not part of its own Taxonrelationship");
258 }
259 }
260 //MISAPPLIED NAMES
261 if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
262 if (taxonRelation.getFromTaxon() == oldTaxon){
263 newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());
264 removableTaxonRels.add(taxonRelation);
265 // oldTaxon.removeTaxonRelation(taxonRelation);
266 }else if(taxonRelation.getToTaxon() == oldTaxon){
267 newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());
268 removableTaxonRels.add(taxonRelation);
269 // oldTaxon.removeTaxonRelation(taxonRelation);
270 }else{
271 logger.warn("Taxon is not part of its own Taxonrelationship");
272 }
273 }
274 //Concept Relationships
275 //FIXME implement
276 // if (taxonRelation.getType().equals(TaxonRelationshipType.MISAPPLIEDNAMEFOR())){
277 // if (taxonRelation.getFromTaxon() == oldTaxon){
278 // newAcceptedTaxon.addMisappliedName(taxonRelation.getToTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());
279 // removableTaxonRels.add(taxonRelation);
280 // }else if(taxonRelation.getToTaxon() == oldTaxon){
281 // newAcceptedTaxon.addMisappliedName(taxonRelation.getFromTaxon(), taxonRelation.getCitation(), taxonRelation.getCitationMicroReference());
282 // removableTaxonRels.add(taxonRelation);
283 // }else{
284 // logger.warn("Taxon is not part of its own Taxonrelationship");
285 // }
286 // }
287 }
288
289 for(TaxonRelationship taxonRel : removableTaxonRels) {
290 oldTaxon.removeTaxonRelation(taxonRel);
291 }
292
293 //Move Descriptions to new Taxon
294 for(TaxonDescription taxDescription : oldTaxon.getDescriptions()){
295 newAcceptedTaxon.addDescription(taxDescription);
296 }
297 //delete old Taxon
298 this.dao.saveOrUpdate(newAcceptedTaxon);
299 // FIXME implement
300 // this.dao.delete(oldTaxon);
301
302 //return
303 // this.dao.flush();
304 return synRel.getSynonym();
305 }
306
307
308 public void generateTitleCache() {
309 generateTitleCache(true);
310 }
311 //TODO
312 public void generateTitleCache(boolean forceProtected) {
313 logger.warn("generateTitleCache not yet fully implemented!");
314 // for (TaxonBase tb : taxonDao.getAllTaxa(null,null)){
315 // logger.warn("Old taxon title: " + tb.getTitleCache());
316 // if (forceProtected || !tb.isProtectedTitleCache() ){
317 // tb.setTitleCache(tb.generateTitle(), false);
318 // taxonDao.update(tb);
319 // logger.warn("New title: " + tb.getTitleCache());
320 // }
321 // }
322
323 }
324
325 @Autowired
326 protected void setDao(ITaxonDao dao) {
327 this.dao = dao;
328 }
329
330 public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
331 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
332
333 List<TaxonBase> results = new ArrayList<TaxonBase>();
334 if(numberOfResults > 0) { // no point checking again
335 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
336 }
337
338 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
339 }
340
341 public Pager<TaxonRelationship> getRelatedTaxa(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
342 Integer numberOfResults = dao.countRelatedTaxa(taxon, type);
343
344 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
345 if(numberOfResults > 0) { // no point checking again
346 results = dao.getRelatedTaxa(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
347 }
348
349 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
350 }
351
352 public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
353 Integer numberOfResults = dao.countSynonyms(taxon, type);
354
355 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
356 if(numberOfResults > 0) { // no point checking again
357 results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
358 }
359
360 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
361 }
362
363 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
364 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
365 return t.getHomotypicSynonymsByHomotypicGroup();
366 }
367
368 public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
369 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
370 List<HomotypicalGroup> hsgl = t.getHeterotypicSynonymyGroups();
371 List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(hsgl.size());
372 for(HomotypicalGroup hsg : hsgl){
373 heterotypicSynonymyGroups.add(hsg.getSynonymsInGroup(t.getSec()));
374 }
375 return heterotypicSynonymyGroups;
376 }
377
378 public Pager<TaxonBase> search(Class<? extends TaxonBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
379 Integer numberOfResults = dao.count(clazz,queryString);
380
381 List<TaxonBase> results = new ArrayList<TaxonBase>();
382 if(numberOfResults > 0) { // no point checking again
383 results = dao.search(clazz,queryString, pageSize, pageNumber, orderHints, propertyPaths);
384 }
385
386 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
387 }
388
389
390 public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {
391
392 List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
393 int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
394 List<TaxonBase> taxa = null;
395
396 // Taxa and synonyms
397
398 int numberTaxaResults = 0;
399
400 if (configurator.isDoTaxa() && configurator.isDoSynonyms()) {
401 taxa = dao.getTaxaByName(configurator.getSearchString(),
402 configurator.getMatchMode(), SelectMode.ALL, configurator.getSec(),
403 configurator.getPageSize(), configurator.getPageNumber());
404 numberTaxaResults =
405 dao.countTaxaByName(configurator.getSearchString(),
406 configurator.getMatchMode(), SelectMode.ALL, configurator.getSec());
407
408 } else if(configurator.isDoTaxa()) {
409 taxa = dao.getTaxaByName(configurator.getSearchString(),
410 configurator.getMatchMode(), SelectMode.TAXA, configurator.getSec(),
411 configurator.getPageSize(), configurator.getPageNumber());
412 numberTaxaResults =
413 dao.countTaxaByName(configurator.getSearchString(),
414 configurator.getMatchMode(), SelectMode.TAXA, configurator.getSec());
415
416 } else if (configurator.isDoSynonyms()) {
417 taxa = dao.getTaxaByName(configurator.getSearchString(),
418 configurator.getMatchMode(), SelectMode.SYNONYMS, configurator.getSec(),
419 configurator.getPageSize(), configurator.getPageNumber());
420 numberTaxaResults =
421 dao.countTaxaByName(configurator.getSearchString(),
422 configurator.getMatchMode(), SelectMode.SYNONYMS, configurator.getSec());
423 }
424
425 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
426
427 results.addAll(taxa);
428
429 numberOfResults += numberTaxaResults;
430
431 // Names without taxa
432
433 if (configurator.isDoNamesWithoutTaxa()) {
434 int numberNameResults = 0;
435 List<? extends TaxonNameBase<?,?>> names =
436 nameDao.findByName(configurator.getSearchString(), configurator.getMatchMode(),
437 configurator.getPageSize(), configurator.getPageNumber(), null);
438 if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
439 if (names.size() > 0) {
440 for (TaxonNameBase<?,?> taxonName : names) {
441 if (taxonName.getTaxonBases().size() == 0) {
442 results.add(taxonName);
443 numberNameResults++;
444 }
445 }
446 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
447 numberOfResults += numberNameResults;
448 }
449 }
450
451 // Taxa from common names
452
453 if (configurator.isDoTaxaByCommonNames()) {
454 int numberCommonNameResults = 0;
455 List<CommonTaxonName> commonTaxonNames =
456 descriptionDao.searchDescriptionByCommonName(configurator.getSearchString(),
457 configurator.getMatchMode(), configurator.getPageSize(), configurator.getPageNumber());
458 if (logger.isDebugEnabled()) { logger.debug(commonTaxonNames.size() + " matching common name(s) found"); }
459 if (commonTaxonNames.size() > 0) {
460 for (CommonTaxonName commonTaxonName : commonTaxonNames) {
461 DescriptionBase description = commonTaxonName.getInDescription();
462 description = HibernateProxyHelper.deproxy(description, DescriptionBase.class);
463 if (description instanceof TaxonDescription) {
464 TaxonDescription taxonDescription = HibernateProxyHelper.deproxy(description, TaxonDescription.class);
465 Taxon taxon = taxonDescription.getTaxon();
466 taxon = HibernateProxyHelper.deproxy(taxon, Taxon.class);
467 if (!results.contains(taxon) && !taxon.isMisappliedName()) {
468 results.add(taxon);
469 numberCommonNameResults++;
470 }
471 } else {
472 logger.warn("Description of " + commonTaxonName.getName() + " is not an instance of TaxonDescription");
473 }
474 }
475 numberOfResults += numberCommonNameResults;
476 }
477 }
478
479 Collections.sort(results);
480 return new DefaultPagerImpl<IdentifiableEntity>
481 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
482 }
483
484 }