move inferred synonyms to service layer
[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.io.IOException;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Set;
19 import java.util.UUID;
20
21
22 import org.apache.log4j.Logger;
23 import org.apache.lucene.analysis.Analyzer;
24 import org.apache.lucene.analysis.standard.StandardAnalyzer;
25 import org.apache.lucene.document.Document;
26 import org.apache.lucene.document.Field;
27 import org.apache.lucene.index.CorruptIndexException;
28 import org.apache.lucene.index.IndexReader;
29 import org.apache.lucene.queryParser.ParseException;
30 import org.apache.lucene.queryParser.QueryParser;
31 import org.apache.lucene.search.HitCollector;
32 import org.apache.lucene.search.IndexSearcher;
33 import org.apache.lucene.search.Query;
34 import org.apache.lucene.search.ScoreDoc;
35 import org.apache.lucene.search.Searcher;
36 import org.apache.lucene.search.TopDocCollector;
37 import org.apache.lucene.search.TopDocs;
38 import org.hibernate.search.FullTextSession;
39 import org.hibernate.search.Search;
40 import org.hibernate.search.SearchFactory;
41 import org.hibernate.search.reader.ReaderProvider;
42 import org.hibernate.search.store.DirectoryProvider;
43 import org.springframework.beans.factory.annotation.Autowired;
44 import org.springframework.stereotype.Service;
45 import org.springframework.transaction.annotation.Propagation;
46 import org.springframework.transaction.annotation.Transactional;
47
48 import sun.print.resources.serviceui;
49
50 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
51 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
52 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
53 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
54 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
55 import eu.etaxonomy.cdm.api.service.pager.Pager;
56 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
57 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
58 import eu.etaxonomy.cdm.api.service.search.SearchResult;
59 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
60 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
61 import eu.etaxonomy.cdm.model.common.CdmBase;
62 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
63 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
64 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
65 import eu.etaxonomy.cdm.model.common.RelationshipBase;
66 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
67 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
68 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
69 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
70 import eu.etaxonomy.cdm.model.description.TaxonDescription;
71 import eu.etaxonomy.cdm.model.description.TextData;
72 import eu.etaxonomy.cdm.model.media.Media;
73 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
74 import eu.etaxonomy.cdm.model.media.MediaUtils;
75 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
76 import eu.etaxonomy.cdm.model.name.NonViralName;
77 import eu.etaxonomy.cdm.model.name.Rank;
78 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
79 import eu.etaxonomy.cdm.model.name.ZoologicalName;
80 import eu.etaxonomy.cdm.model.reference.Reference;
81 import eu.etaxonomy.cdm.model.taxon.Classification;
82 import eu.etaxonomy.cdm.model.taxon.Synonym;
83 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
84 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
85 import eu.etaxonomy.cdm.model.taxon.Taxon;
86 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
87 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
88 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
89 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
90 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
91 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
92 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
93 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
94 import eu.etaxonomy.cdm.persistence.query.MatchMode;
95 import eu.etaxonomy.cdm.persistence.query.OrderHint;
96 import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
97 import eu.etaxonomy.cdm.search.LuceneSearch;
98 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
99
100
101 /**
102 * @author a.kohlbecker
103 * @date 10.09.2010
104 *
105 */
106 @Service
107 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
108 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
109 private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
110
111 @Autowired
112 private ITaxonNameDao nameDao;
113
114 @Autowired
115 private ISearchResultBuilder searchResultBuilder;
116
117 @Autowired
118 private IOrderedTermVocabularyDao orderedVocabularyDao;
119
120 @Autowired
121 private INameService nameService;
122
123 /**
124 * Constructor
125 */
126 public TaxonServiceImpl(){
127 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
128 }
129
130 /**
131 * FIXME Candidate for harmonization
132 * rename searchByName ?
133 */
134 public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
135 return dao.getTaxaByName(name, sec);
136 }
137
138 /**
139 * FIXME Candidate for harmonization
140 * list(Synonym.class, ...)
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 * FIXME Candidate for harmonization
150 * list(Taxon.class, ...)
151 * (non-Javadoc)
152 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
153 */
154 public List<Taxon> getAllTaxa(int limit, int start) {
155 return dao.getAllTaxa(limit, start);
156 }
157
158 /**
159 * FIXME Candidate for harmonization
160 * merge with getRootTaxa(Reference sec, ..., ...)
161 * (non-Javadoc)
162 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
163 */
164 public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
165 if (cdmFetch == null){
166 cdmFetch = CdmFetch.NO_FETCH();
167 }
168 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
169 }
170
171
172 /* (non-Javadoc)
173 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
174 */
175 public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
176 return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
177 }
178
179 /* (non-Javadoc)
180 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
181 */
182 public List<RelationshipBase> getAllRelationships(int limit, int start){
183 return dao.getAllRelationships(limit, start);
184 }
185
186 /**
187 * FIXME Candidate for harmonization
188 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
189 */
190 @Deprecated
191 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
192
193 String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
194 UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
195 OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
196 (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
197 return taxonRelTypeVocabulary;
198 }
199
200
201
202 /*
203 * (non-Javadoc)
204 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
205 */
206 @Transactional(readOnly = false)
207 public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
208
209 TaxonNameBase<?,?> synonymName = synonym.getName();
210 synonymName.removeTaxonBase(synonym);
211 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
212 taxonName.removeTaxonBase(acceptedTaxon);
213
214 synonym.setName(taxonName);
215 acceptedTaxon.setName(synonymName);
216
217 // the accepted taxon needs a new uuid because the concept has changed
218 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
219 //acceptedTaxon.setUuid(UUID.randomUUID());
220 }
221
222
223 /* (non-Javadoc)
224 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
225 */
226 //TODO correct delete handling still needs to be implemented / checked
227 @Override
228 @Transactional(readOnly = false)
229 public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
230
231 TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
232 TaxonNameBase<?,?> synonymName = synonym.getName();
233 HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
234
235 //check synonym is not homotypic
236 if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
237 String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
238 throw new HomotypicalGroupChangeException(message);
239 }
240
241 Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
242
243 SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
244 List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
245
246 for (Synonym heteroSynonym : heteroSynonyms){
247 if (synonym.equals(heteroSynonym)){
248 acceptedTaxon.removeSynonym(heteroSynonym, false);
249 }else{
250 //move synonyms in same homotypic group to new accepted taxon
251 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
252 }
253 }
254
255 //synonym.getName().removeTaxonBase(synonym);
256 //TODO correct delete handling still needs to be implemented / checked
257 if (deleteSynonym){
258 // deleteSynonym(synonym, taxon, false);
259 try {
260 this.dao.flush();
261 this.delete(synonym);
262
263 } catch (Exception e) {
264 logger.info("Can't delete old synonym from database");
265 }
266 }
267
268 return newAcceptedTaxon;
269 }
270
271
272 public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
273
274 // Get name from synonym
275 TaxonNameBase<?, ?> synonymName = synonym.getName();
276
277 // remove synonym from taxon
278 toTaxon.removeSynonym(synonym);
279
280 // Create a taxon with synonym name
281 Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
282
283 // Add taxon relation
284 fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
285
286 // since we are swapping names, we have to detach the name from the synonym completely.
287 // Otherwise the synonym will still be in the list of typified names.
288 synonym.getName().removeTaxonBase(synonym);
289
290 return fromTaxon;
291 }
292
293
294 /* (non-Javadoc)
295 * @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)
296 */
297 @Transactional(readOnly = false)
298 @Override
299 public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
300 boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
301 // Get synonym name
302 TaxonNameBase synonymName = synonym.getName();
303 HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
304
305
306 // Switch groups
307 oldHomotypicalGroup.removeTypifiedName(synonymName);
308 newHomotypicalGroup.addTypifiedName(synonymName);
309
310 //remove existing basionym relationships
311 synonymName.removeBasionyms();
312
313 //add basionym relationship
314 if (setBasionymRelationIfApplicable){
315 Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
316 for (TaxonNameBase basionym : basionyms){
317 synonymName.addBasionym(basionym);
318 }
319 }
320
321 //set synonym relationship correctly
322 // SynonymRelationship relToTaxon = null;
323 boolean relToTargetTaxonExists = false;
324 Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
325 for (SynonymRelationship rel : existingRelations){
326 Taxon acceptedTaxon = rel.getAcceptedTaxon();
327 boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
328 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
329 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
330 SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
331 rel.setType(newRelationType);
332 //TODO handle citation and microCitation
333
334 if (isTargetTaxon){
335 relToTargetTaxonExists = true;
336 }else{
337 if (removeFromOtherTaxa){
338 acceptedTaxon.removeSynonym(synonym, false);
339 }else{
340 //do nothing
341 }
342 }
343 }
344 if (targetTaxon != null && ! relToTargetTaxonExists ){
345 Taxon acceptedTaxon = targetTaxon;
346 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
347 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
348 SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
349 //TODO handle citation and microCitation
350 Reference citation = null;
351 String microCitation = null;
352 acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
353 }
354
355 }
356
357
358 /* (non-Javadoc)
359 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
360 */
361 @Override
362 public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
363 if (clazz == null){
364 clazz = TaxonBase.class;
365 }
366 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
367 }
368
369 @Autowired
370 protected void setDao(ITaxonDao dao) {
371 this.dao = dao;
372 }
373
374 /* (non-Javadoc)
375 * @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)
376 */
377 public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
378 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
379
380 List<TaxonBase> results = new ArrayList<TaxonBase>();
381 if(numberOfResults > 0) { // no point checking again
382 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
383 }
384
385 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
386 }
387
388 /* (non-Javadoc)
389 * @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)
390 */
391 public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
392 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
393
394 List<TaxonBase> results = new ArrayList<TaxonBase>();
395 if(numberOfResults > 0) { // no point checking again
396 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
397 }
398
399 return results;
400 }
401
402 /* (non-Javadoc)
403 * @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)
404 */
405 public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
406 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
407
408 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
409 if(numberOfResults > 0) { // no point checking again
410 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
411 }
412 return results;
413 }
414
415 /* (non-Javadoc)
416 * @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)
417 */
418 public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
419 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
420
421 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
422 if(numberOfResults > 0) { // no point checking again
423 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
424 }
425 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
426 }
427
428 /* (non-Javadoc)
429 * @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)
430 */
431 public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
432 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
433
434 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
435 if(numberOfResults > 0) { // no point checking again
436 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
437 }
438 return results;
439 }
440
441 /* (non-Javadoc)
442 * @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)
443 */
444 public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
445 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
446
447 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
448 if(numberOfResults > 0) { // no point checking again
449 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
450 }
451 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
452 }
453
454 /* (non-Javadoc)
455 * @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)
456 */
457 public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
458 Integer numberOfResults = dao.countSynonyms(taxon, type);
459
460 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
461 if(numberOfResults > 0) { // no point checking again
462 results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
463 }
464
465 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
466 }
467
468 /* (non-Javadoc)
469 * @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)
470 */
471 public Pager<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
472 Integer numberOfResults = dao.countSynonyms(synonym, type);
473
474 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
475 if(numberOfResults > 0) { // no point checking again
476 results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
477 }
478
479 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
480 }
481
482 /* (non-Javadoc)
483 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
484 */
485 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
486 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
487 return t.getHomotypicSynonymsByHomotypicGroup();
488 }
489
490 /* (non-Javadoc)
491 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
492 */
493 public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
494 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
495 List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
496 List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
497 for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
498 heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
499 }
500 return heterotypicSynonymyGroups;
501 }
502
503 public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){
504
505 List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();
506 // Class<? extends TaxonBase> clazz = null;
507 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
508 // clazz = TaxonBase.class;
509 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
510 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
511 // } else if(configurator.isDoTaxa()) {
512 // clazz = Taxon.class;
513 // //propertyPath = configurator.getTaxonPropertyPath();
514 // } else if (configurator.isDoSynonyms()) {
515 // clazz = Synonym.class;
516 // //propertyPath = configurator.getSynonymPropertyPath();
517 // }
518
519
520 result = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
521 return result;
522 }
523
524 /* (non-Javadoc)
525 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
526 */
527 public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {
528
529 List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
530 int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
531 List<TaxonBase> taxa = null;
532
533 // Taxa and synonyms
534 long numberTaxaResults = 0L;
535
536
537 List<String> propertyPath = new ArrayList<String>();
538 if(configurator.getTaxonPropertyPath() != null){
539 propertyPath.addAll(configurator.getTaxonPropertyPath());
540 }
541
542
543 if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){
544 if(configurator.getPageSize() != null){ // no point counting if we need all anyway
545 numberTaxaResults =
546 dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
547 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
548 configurator.getNamedAreas());
549 }
550
551 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
552 taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
553 configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),
554 configurator.getMatchMode(), configurator.getNamedAreas(),
555 configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
556 }
557 }
558
559 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
560
561 if(taxa != null){
562 results.addAll(taxa);
563 }
564
565 numberOfResults += numberTaxaResults;
566
567 // Names without taxa
568 if (configurator.isDoNamesWithoutTaxa()) {
569 int numberNameResults = 0;
570
571 List<? extends TaxonNameBase<?,?>> names =
572 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
573 configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
574 if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
575 if (names.size() > 0) {
576 for (TaxonNameBase<?,?> taxonName : names) {
577 if (taxonName.getTaxonBases().size() == 0) {
578 results.add(taxonName);
579 numberNameResults++;
580 }
581 }
582 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
583 numberOfResults += numberNameResults;
584 }
585 }
586
587 // Taxa from common names
588
589 if (configurator.isDoTaxaByCommonNames()) {
590 taxa = new ArrayList<TaxonBase>();
591 numberTaxaResults = 0;
592 if(configurator.getPageSize() != null){// no point counting if we need all anyway
593 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
594 }
595 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
596 List<Object[]> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
597 for( Object[] entry : commonNameResults ) {
598 taxa.add((TaxonBase) entry[0]);
599 }
600 }
601 if(taxa != null){
602 results.addAll(taxa);
603 }
604 numberOfResults += numberTaxaResults;
605
606 }
607
608 return new DefaultPagerImpl<IdentifiableEntity>
609 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
610 }
611
612 public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
613 return dao.getUuidAndTitleCache();
614 }
615
616 /* (non-Javadoc)
617 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
618 */
619 public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
620 List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
621 taxon = (Taxon)dao.load(taxon.getUuid());
622 Set<TaxonDescription> descriptions = taxon.getDescriptions();
623 for (TaxonDescription taxDesc: descriptions){
624 Set<DescriptionElementBase> elements = taxDesc.getElements();
625 for (DescriptionElementBase descElem: elements){
626 for(Media media : descElem.getMedia()){
627
628 //find the best matching representation
629 medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
630
631 }
632 }
633 }
634 return medRep;
635 }
636
637 /* (non-Javadoc)
638 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
639 */
640 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
641 return this.dao.findById(listOfIDs);
642 }
643
644 /* (non-Javadoc)
645 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
646 */
647 public int countAllRelationships() {
648 return this.dao.countAllRelationships();
649 }
650
651
652
653
654 /* (non-Javadoc)
655 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
656 */
657 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
658 return this.dao.findIdenticalTaxonNames(propertyPath);
659 }
660
661
662 /* (non-Javadoc)
663 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
664 */
665 @Transactional(readOnly = false)
666 @Override
667 public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
668 if (synonym == null){
669 return;
670 }
671 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
672
673 //remove synonymRelationship
674 Set<Taxon> taxonSet = new HashSet<Taxon>();
675 if (taxon != null){
676 taxonSet.add(taxon);
677 }else{
678 taxonSet.addAll(synonym.getAcceptedTaxa());
679 }
680 for (Taxon relatedTaxon : taxonSet){
681 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
682 relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
683 }
684 this.saveOrUpdate(synonym);
685
686 //TODO remove name from homotypical group?
687
688 //remove synonym (if necessary)
689 if (synonym.getSynonymRelations().isEmpty()){
690 TaxonNameBase<?,?> name = synonym.getName();
691 synonym.setName(null);
692 dao.delete(synonym);
693
694 //remove name if possible (and required)
695 if (name != null && removeNameIfPossible){
696 try{
697 nameService.delete(name, new NameDeletionConfigurator());
698 }catch (DataChangeNoRollbackException ex){
699 if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");
700 }
701 }
702 }
703 }
704
705
706 /* (non-Javadoc)
707 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
708 */
709 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
710
711 return this.dao.findIdenticalNamesNew(propertyPath);
712 }
713
714 /* (non-Javadoc)
715 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
716 */
717 public String getPhylumName(TaxonNameBase name){
718 return this.dao.getPhylumName(name);
719 }
720
721 /* (non-Javadoc)
722 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
723 */
724 public long deleteSynonymRelationships(Synonym syn) {
725 return dao.deleteSynonymRelationships(syn, null);
726 }
727
728
729 /* (non-Javadoc)
730 * @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)
731 */
732 public List<SynonymRelationship> listSynonymRelationships(
733 TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
734 List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
735 Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
736
737 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
738 if(numberOfResults > 0) { // no point checking again
739 results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
740 }
741 return results;
742 }
743
744 /* (non-Javadoc)
745 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
746 */
747 @Override
748 public Taxon findBestMatchingTaxon(String taxonName) {
749 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
750 config.setTaxonNameTitle(taxonName);
751 return findBestMatchingTaxon(config);
752 }
753
754
755
756 @Override
757 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
758
759 Taxon bestCandidate = null;
760 try{
761 // 1. search for acceptet taxa
762 List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
763 boolean bestCandidateMatchesSecUuid = false;
764 boolean bestCandidateIsInClassification = false;
765 int countEqualCandidates = 0;
766 for(TaxonBase taxonBaseCandidate : taxonList){
767 if(taxonBaseCandidate instanceof Taxon){
768 Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
769 boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
770 if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
771 continue;
772 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
773 bestCandidate = newCanditate;
774 countEqualCandidates = 1;
775 bestCandidateMatchesSecUuid = true;
776 continue;
777 }
778
779 boolean newCandidateInClassification = isInClassification(newCanditate, config);
780 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
781 continue;
782 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
783 bestCandidate = newCanditate;
784 countEqualCandidates = 1;
785 bestCandidateIsInClassification = true;
786 continue;
787 }
788 if (bestCandidate == null){
789 bestCandidate = newCanditate;
790 countEqualCandidates = 1;
791 continue;
792 }
793
794 }else{ //not Taxon.class
795 continue;
796 }
797 countEqualCandidates++;
798
799 }
800 if (bestCandidate != null){
801 if(countEqualCandidates > 1){
802 logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
803 return bestCandidate;
804 } else {
805 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
806 return bestCandidate;
807 }
808 }
809
810
811 // 2. search for synonyms
812 if (config.isIncludeSynonyms()){
813 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
814 for(TaxonBase taxonBase : synonymList){
815 if(taxonBase instanceof Synonym){
816 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
817 Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
818 if(!acceptetdCandidates.isEmpty()){
819 bestCandidate = acceptetdCandidates.iterator().next();
820 if(acceptetdCandidates.size() == 1){
821 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
822 return bestCandidate;
823 } else {
824 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
825 return bestCandidate;
826 }
827 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
828 }
829 }
830 }
831 }
832
833 } catch (Exception e){
834 logger.error(e);
835 }
836
837 return bestCandidate;
838 }
839
840 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
841 UUID configClassificationUuid = config.getClassificationUuid();
842 if (configClassificationUuid == null){
843 return false;
844 }
845 for (TaxonNode node : taxon.getTaxonNodes()){
846 UUID classUuid = node.getClassification().getUuid();
847 if (configClassificationUuid.equals(classUuid)){
848 return true;
849 }
850 }
851 return false;
852 }
853
854 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
855 UUID configSecUuid = config.getSecUuid();
856 if (configSecUuid == null){
857 return false;
858 }
859 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
860 return configSecUuid.equals(taxonSecUuid);
861 }
862
863 /* (non-Javadoc)
864 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
865 */
866 @Override
867 public Synonym findBestMatchingSynonym(String taxonName) {
868 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
869 if(! synonymList.isEmpty()){
870 Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
871 if(synonymList.size() == 1){
872 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
873 return result;
874 } else {
875 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
876 return result;
877 }
878 }
879 return null;
880 }
881
882
883 /* (non-Javadoc)
884 * @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)
885 */
886 @Override
887 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
888 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
889
890 Synonym synonym = oldSynonymRelation.getSynonym();
891 Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
892 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
893 TaxonNameBase<?,?> synonymName = synonym.getName();
894 TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
895 //set default relationship type
896 if (newSynonymRelationshipType == null){
897 newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
898 }
899 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
900
901 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
902 int hgSize = homotypicGroup.getTypifiedNames().size();
903 boolean isSingleInGroup = !(hgSize > 1);
904
905 if (! isSingleInGroup){
906 boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
907 boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
908 if (isHomotypicToAccepted){
909 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.";
910 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
911 message = String.format(message, homotypicRelatives);
912 throw new HomotypicalGroupChangeException(message);
913 }
914 if (! moveHomotypicGroup){
915 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.";
916 throw new HomotypicalGroupChangeException(message);
917 }
918 }else{
919 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
920 }
921 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
922
923 SynonymRelationship result = null;
924 //move all synonyms to new taxon
925 List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
926 for (Synonym syn: homotypicSynonyms){
927 Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
928 for (SynonymRelationship synRelation : synRelations){
929 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
930 Reference<?> newReference = reference;
931 if (newReference == null && keepReference){
932 newReference = synRelation.getCitation();
933 }
934 String newRefDetail = referenceDetail;
935 if (newRefDetail == null && keepReference){
936 newRefDetail = synRelation.getCitationMicroReference();
937 }
938 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
939 fromTaxon.removeSynonymRelation(synRelation, false);
940 //
941 //change homotypic group of synonym if relType is 'homotypic'
942 // if (newRelTypeIsHomotypic){
943 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
944 // }
945 //set result
946 if (synRelation.equals(oldSynonymRelation)){
947 result = newSynRelation;
948 }
949 }
950 }
951
952 }
953 saveOrUpdate(newTaxon);
954 //Assert that there is a result
955 if (result == null){
956 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
957 throw new IllegalStateException(message);
958 }
959 return result;
960 }
961
962 /* (non-Javadoc)
963 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
964 */
965 @Override
966 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
967 return dao.getUuidAndTitleCacheTaxon();
968 }
969
970 /* (non-Javadoc)
971 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
972 */
973 @Override
974 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
975 return dao.getUuidAndTitleCacheSynonym();
976 }
977
978 @Override
979 public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(Class<? extends DescriptionElementBase> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
980 List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
981
982 String luceneQueryTemplate = "titleCache:%1$s OR multilanguageText.text:%1$s OR name:%1$s";
983 String luceneQuery = String.format(luceneQueryTemplate, queryString);
984
985 LuceneSearch luceneSearch = new LuceneSearch(getSession(), clazz);
986 TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQuery);
987 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id");
988
989 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);
990
991 }
992
993 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type){
994 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
995 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
996
997 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
998 UUID uuid;
999
1000 uuid= taxon.getName().getUuid();
1001 ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);
1002 String epithetOfTaxon = null;
1003 String infragenericEpithetOfTaxon = null;
1004 String infraspecificEpithetOfTaxon = null;
1005 if (taxonName.isSpecies()){
1006 epithetOfTaxon= taxonName.getSpecificEpithet();
1007 } else if (taxonName.isInfraGeneric()){
1008 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1009 } else if (taxonName.isInfraSpecific()){
1010 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1011 }
1012 String genusOfTaxon = taxonName.getGenusOrUninomial();
1013 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1014 List<String> taxonNames = new ArrayList<String>();
1015
1016 for (TaxonNode node: nodes){
1017 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1018 // List<String> synonymsEpithet = new ArrayList<String>();
1019
1020 if (node.getClassification().equals(classification)){
1021 if (!node.isTopmostNode()){
1022 TaxonNode parent = (TaxonNode)node.getParent();
1023 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1024 TaxonNameBase parentName = parent.getTaxon().getName();
1025 ZoologicalName zooParentName = (ZoologicalName)HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1026 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1027 Rank rankOfTaxon = taxonName.getRank();
1028
1029
1030 //create inferred synonyms for species, subspecies or subgenus
1031 if (parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())){
1032
1033 Synonym inferredEpithet;
1034 Synonym inferredGenus = null;
1035 Synonym potentialCombination;
1036
1037 List<String> propertyPaths = new ArrayList<String>();
1038 propertyPaths.add("synonym");
1039 propertyPaths.add("synonym.name");
1040 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1041 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1042
1043 List<SynonymRelationship> synonymRelationshipsOfGenus = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1044 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1045
1046 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1047
1048 for (SynonymRelationship synonymRelationOfGenus:synonymRelationshipsOfGenus){
1049 TaxonNameBase synName;
1050 ZoologicalName inferredSynName;
1051 Synonym syn = synonymRelationOfGenus.getSynonym();
1052 HibernateProxyHelper.deproxy(syn);
1053
1054 // Determine the idInSource
1055 String idInSource = getIdInSource(syn);
1056
1057 // Determine the sourceReference
1058 Reference sourceReference = syn.getSec();
1059
1060 synName = syn.getName();
1061 ZoologicalName zooName = getZoologicalName(synName.getUuid(), zooHashMap);
1062 String synGenusName = zooName.getGenusOrUninomial();
1063 String synInfraGenericEpithet = null;
1064 String synSpecificEpithet = null;
1065 if (zooName.isInfraGeneric()){
1066 synInfraGenericEpithet = zooName.getInfraGenericEpithet();
1067 } else if (zooName.isSpecies()){
1068 synSpecificEpithet = zooName.getSpecificEpithet();
1069 }
1070
1071 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1072 synonymsGenus.put(synGenusName, idInSource);
1073 }*/
1074
1075 inferredSynName = ZoologicalName.NewInstance(rankOfTaxon);
1076
1077 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1078 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
1079 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
1080 }
1081 inferredSynName.setGenusOrUninomial(synGenusName);
1082 if (inferredSynName.isSpecies()){
1083 if (parentName.isInfraGeneric()){
1084 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
1085 }
1086 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1087 } else if (inferredSynName.isInfraGeneric()) {
1088 inferredSynName.setInfraGenericEpithet(infragenericEpithetOfTaxon);
1089 } else if (inferredSynName.isInfraSpecific()){
1090 inferredSynName.setSpecificEpithet(synSpecificEpithet);
1091 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
1092 }
1093
1094 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1095
1096 // Set the sourceReference
1097 inferredEpithet.setSec(sourceReference);
1098
1099 // Add the original source
1100 if (idInSource != null) {
1101 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1102
1103 // Add the citation
1104 Reference citation = getCitation(syn);
1105 if (citation != null) {
1106 originalSource.setCitation(citation);
1107 inferredEpithet.addSource(originalSource);
1108 }
1109 }
1110
1111 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_GENUS_OF());
1112 inferredSynonyms.add(inferredEpithet);
1113 inferredSynName.generateTitle();
1114 zooHashMap.put(inferredSynName.getUuid(), inferredSynName);
1115 taxonNames.add(inferredSynName.getNameCache());
1116 }
1117
1118 if (!taxonNames.isEmpty()){
1119 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1120 ZoologicalName name;
1121 if (!synNotInCDM.isEmpty()){
1122 inferredSynonymsToBeRemoved.clear();
1123
1124 for (Synonym syn :inferredSynonyms){
1125 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1126 if (!synNotInCDM.contains(name.getNameCache())){
1127 inferredSynonymsToBeRemoved.add(syn);
1128 }
1129 }
1130
1131 // Remove identified Synonyms from inferredSynonyms
1132 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1133 inferredSynonyms.remove(synonym);
1134 }
1135 }
1136 }
1137
1138 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1139
1140
1141 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1142 TaxonNameBase synName;
1143 ZoologicalName inferredSynName;
1144
1145 Synonym syn = synonymRelationOfTaxon.getSynonym();
1146 synName =syn.getName();
1147 HibernateProxyHelper.deproxy(syn);
1148
1149 // Determine the idInSource
1150 String idInSource = getIdInSource(syn);
1151
1152 // Determine the sourceReference
1153 Reference sourceReference = syn.getSec();
1154
1155 synName = syn.getName();
1156 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1157 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1158 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1159 synonymsEpithet.add(synSpeciesEpithetName);
1160 }*/
1161
1162 inferredSynName = ZoologicalName.NewInstance(rankOfTaxon);
1163 //TODO:differ between parent is genusa and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
1164
1165
1166 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1167
1168 if (taxonName.isSpecies()){
1169 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1170 if (zooParentName.isInfraGeneric()){
1171 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1172 }
1173 }
1174 if (taxonName.isInfraSpecific()){
1175 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1176 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1177 }
1178 if (taxonName.isInfraGeneric()){
1179 inferredSynName.setInfraGenericEpithet(synZooName.getInfraGenericEpithet());
1180 }
1181
1182 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1183
1184 // Set the sourceReference
1185 inferredGenus.setSec(sourceReference);
1186
1187 // Add the original source
1188 if (idInSource != null) {
1189 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredGenusOf", syn.getSec(), null);
1190
1191 // Add the citation
1192 Reference citation = getCitation(syn);
1193 if (citation != null) {
1194 originalSource.setCitation(citation);
1195 inferredGenus.addSource(originalSource);
1196 }
1197 }
1198
1199 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_EPITHET_OF());
1200 inferredSynonyms.add(inferredGenus);
1201 inferredSynName.generateTitle();
1202 zooHashMap.put(inferredSynName.getUuid(), inferredSynName);
1203 taxonNames.add(inferredSynName.getNameCache());
1204 }
1205
1206 if (!taxonNames.isEmpty()){
1207 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1208 ZoologicalName name;
1209 if (!synNotInCDM.isEmpty()){
1210 inferredSynonymsToBeRemoved.clear();
1211
1212 for (Synonym syn :inferredSynonyms){
1213 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1214 if (!synNotInCDM.contains(name.getNameCache())){
1215 inferredSynonymsToBeRemoved.add(syn);
1216 }
1217 }
1218
1219 // Remove identified Synonyms from inferredSynonyms
1220 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1221 inferredSynonyms.remove(synonym);
1222 }
1223 }
1224 }
1225
1226 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1227
1228 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1229 ZoologicalName inferredSynName;
1230 //for all synonyms of the parent...
1231 for (SynonymRelationship synonymRelationOfGenus:synonymRelationshipsOfGenus){
1232 TaxonNameBase synName;
1233 Synonym synParent = synonymRelationOfGenus.getSynonym();
1234 synName = synParent.getName();
1235
1236 HibernateProxyHelper.deproxy(synParent);
1237
1238 // Set the sourceReference
1239 sourceReference = synParent.getSec();
1240
1241 // Determine the idInSource
1242 String idInSourceParent = getIdInSource(synParent);
1243
1244 ZoologicalName parentZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1245 String synParentGenus = parentZooName.getGenusOrUninomial();
1246 String synParentInfragenericName = null;
1247 String synParentSpecificEpithet = null;
1248
1249 if (parentZooName.isInfraGeneric()){
1250 synParentInfragenericName = parentZooName.getInfraGenericEpithet();
1251 }
1252 if (parentZooName.isSpecies()){
1253 synParentSpecificEpithet = parentZooName.getSpecificEpithet();
1254 }
1255
1256 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1257 synonymsGenus.put(synGenusName, idInSource);
1258 }*/
1259
1260 //for all synonyms of the taxon
1261
1262 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1263
1264 Synonym syn = synonymRelationOfTaxon.getSynonym();
1265 HibernateProxyHelper.deproxy(syn);
1266
1267 // Set sourceReference
1268 sourceReference = syn.getSec();
1269
1270 ZoologicalName zooName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1271 String synTaxonSpecificEpithet = zooName.getSpecificEpithet();
1272 String synTaxonInfragenericName = null;
1273 String synTaxonInfraSpecificName= null;
1274
1275 if (parentZooName.isSpecies()){
1276 synTaxonInfraSpecificName = zooName.getInfraSpecificEpithet();
1277 }
1278 if (zooName.isInfraGeneric()){
1279 synTaxonInfragenericName = zooName.getInfraGenericEpithet();
1280 }
1281
1282 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1283 synonymsEpithet.add(epithetName);
1284 }*/
1285
1286 //create potential combinations...
1287 inferredSynName = ZoologicalName.NewInstance(rankOfTaxon);
1288
1289 inferredSynName.setGenusOrUninomial(synParentGenus);
1290 if (taxonName.isSpecies()){
1291 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1292 if (parentName.isInfraGeneric()){
1293 inferredSynName.setInfraGenericEpithet(synTaxonInfragenericName);
1294 }
1295 }
1296 if (taxonName.isInfraSpecific()){
1297 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1298 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1299 }
1300 if (taxonName.isInfraGeneric()){
1301 inferredSynName.setInfraGenericEpithet(synTaxonInfragenericName);
1302 }
1303
1304
1305 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1306
1307 // Set the sourceReference
1308 potentialCombination.setSec(sourceReference);
1309
1310
1311 // Determine the idInSource
1312 String idInSourceTaxon = getIdInSource(syn);
1313
1314
1315 if (idInSourceTaxon != null) {
1316 IdentifiableSource originalSourceTaxon = IdentifiableSource.NewInstance(idInSourceTaxon, "PotentialCombinationOf", sourceReference, null);
1317 if (sourceReference != null) {
1318 originalSourceTaxon.setCitation(sourceReference);
1319 potentialCombination.addSource(originalSourceTaxon);
1320 }
1321 }
1322 if (idInSourceParent != null){
1323 IdentifiableSource originalSourceParent = IdentifiableSource.NewInstance(idInSourceParent, "PotentialCombinationOf", sourceReference, null);
1324 if (sourceReference != null) {
1325 originalSourceParent.setCitation(sourceReference);
1326 potentialCombination.addSource(originalSourceParent);
1327 }
1328 }
1329
1330 // Add the citation
1331
1332
1333
1334 inferredSynonyms.add(potentialCombination);
1335 inferredSynName.generateTitle();
1336 zooHashMap.put(inferredSynName.getUuid(), inferredSynName);
1337 taxonNames.add(inferredSynName.getNameCache());
1338
1339 }
1340
1341 }
1342 if (!taxonNames.isEmpty()){
1343 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1344 ZoologicalName name;
1345 if (!synNotInCDM.isEmpty()){
1346 inferredSynonymsToBeRemoved.clear();
1347 for (Synonym syn :inferredSynonyms){
1348 try{
1349 name = (ZoologicalName) syn.getName();
1350 }catch (ClassCastException e){
1351 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1352 }
1353 if (!synNotInCDM.contains(name.getNameCache())){
1354 inferredSynonymsToBeRemoved.add(syn);
1355 }
1356 }
1357 // Remove identified Synonyms from inferredSynonyms
1358 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1359 inferredSynonyms.remove(synonym);
1360 }
1361 }
1362 }
1363 }
1364 }else {
1365 logger.info("The synonymrelationship type is not defined.");
1366 return null;
1367 }
1368 }
1369 }
1370
1371 }
1372
1373 return inferredSynonyms;
1374 }
1375
1376 /**
1377 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1378 * Very likely only useful for createInferredSynonyms().
1379 * @param uuid
1380 * @param zooHashMap
1381 * @return
1382 */
1383 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
1384 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
1385 if (taxonName == null) {
1386 taxonName = zooHashMap.get(uuid);
1387 }
1388 return taxonName;
1389 }
1390
1391 /**
1392 * Returns the idInSource for a given Synonym.
1393 * @param syn
1394 */
1395 private String getIdInSource(Synonym syn) {
1396 String idInSource = null;
1397 Set<IdentifiableSource> sources = syn.getSources();
1398 if (sources.size() == 1) {
1399 IdentifiableSource source = sources.iterator().next();
1400 if (source != null) {
1401 idInSource = source.getIdInSource();
1402 }
1403 } else if (sources.size() > 1) {
1404 int count = 1;
1405 idInSource = "";
1406 for (IdentifiableSource source : sources) {
1407 idInSource += source.getIdInSource();
1408 if (count < sources.size()) {
1409 idInSource += "; ";
1410 }
1411 count++;
1412 }
1413 }
1414
1415 return idInSource;
1416 }
1417
1418
1419 /**
1420 * Returns the citation for a given Synonym.
1421 * @param syn
1422 */
1423 private Reference getCitation(Synonym syn) {
1424 Reference citation = null;
1425 Set<IdentifiableSource> sources = syn.getSources();
1426 if (sources.size() == 1) {
1427 IdentifiableSource source = sources.iterator().next();
1428 if (source != null) {
1429 citation = source.getCitation();
1430 }
1431 } else if (sources.size() > 1) {
1432 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
1433 }
1434
1435 return citation;
1436 }
1437
1438 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree){
1439 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1440
1441 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF()));
1442 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF()));
1443 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF()));
1444
1445 return inferredSynonyms;
1446 }
1447
1448
1449
1450 }