missing bits for #476: Implement free-text search methods for TaxonBase and TextData
[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 import org.apache.log4j.Logger;
22 import org.apache.lucene.index.CorruptIndexException;
23 import org.apache.lucene.queryParser.ParseException;
24 import org.apache.lucene.search.TopDocs;
25 import org.hibernate.criterion.Criterion;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.stereotype.Service;
28 import org.springframework.transaction.annotation.Propagation;
29 import org.springframework.transaction.annotation.Transactional;
30
31 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
32 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
33 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
34 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
35 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
36 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
37 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
38 import eu.etaxonomy.cdm.api.service.pager.Pager;
39 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
40 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
41 import eu.etaxonomy.cdm.api.service.search.SearchResult;
42 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
43 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
44 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
45 import eu.etaxonomy.cdm.model.common.CdmBase;
46 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
47 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
48 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
49 import eu.etaxonomy.cdm.model.common.Language;
50 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
51 import eu.etaxonomy.cdm.model.common.RelationshipBase;
52 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
53 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
54 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
55 import eu.etaxonomy.cdm.model.description.IIdentificationKey;
56 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
57 import eu.etaxonomy.cdm.model.description.TaxonDescription;
58 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
59 import eu.etaxonomy.cdm.model.media.Media;
60 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
61 import eu.etaxonomy.cdm.model.media.MediaUtils;
62 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
63 import eu.etaxonomy.cdm.model.name.NonViralName;
64 import eu.etaxonomy.cdm.model.name.Rank;
65 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
66 import eu.etaxonomy.cdm.model.name.ZoologicalName;
67 import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
68 import eu.etaxonomy.cdm.model.reference.Reference;
69 import eu.etaxonomy.cdm.model.taxon.Classification;
70 import eu.etaxonomy.cdm.model.taxon.Synonym;
71 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
72 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
73 import eu.etaxonomy.cdm.model.taxon.Taxon;
74 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
75 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
76 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
77 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
78 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
79 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
80 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
81 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
82 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
83 import eu.etaxonomy.cdm.persistence.query.MatchMode;
84 import eu.etaxonomy.cdm.persistence.query.OrderHint;
85 import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
86 import eu.etaxonomy.cdm.search.LuceneSearch;
87 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
88
89
90 /**
91 * @author a.kohlbecker
92 * @date 10.09.2010
93 *
94 */
95 @Service
96 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
97 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
98 private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
99
100 public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";
101
102 public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";
103
104 public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";
105
106
107 @Autowired
108 private ITaxonNameDao nameDao;
109
110 @Autowired
111 private ISearchResultBuilder searchResultBuilder;
112
113 @Autowired
114 private INameService nameService;
115
116 @Autowired
117 private ICdmGenericDao genericDao;
118
119 @Autowired
120 private IDescriptionService descriptionService;
121
122 @Autowired
123 private IOrderedTermVocabularyDao orderedVocabularyDao;
124
125 /**
126 * Constructor
127 */
128 public TaxonServiceImpl(){
129 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
130 }
131
132 /**
133 * FIXME Candidate for harmonization
134 * rename searchByName ?
135 */
136 public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
137 return dao.getTaxaByName(name, sec);
138 }
139
140 /**
141 * FIXME Candidate for harmonization
142 * list(Synonym.class, ...)
143 * (non-Javadoc)
144 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
145 */
146 public List<Synonym> getAllSynonyms(int limit, int start) {
147 return dao.getAllSynonyms(limit, start);
148 }
149
150 /**
151 * FIXME Candidate for harmonization
152 * list(Taxon.class, ...)
153 * (non-Javadoc)
154 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
155 */
156 public List<Taxon> getAllTaxa(int limit, int start) {
157 return dao.getAllTaxa(limit, start);
158 }
159
160 /**
161 * FIXME Candidate for harmonization
162 * merge with getRootTaxa(Reference sec, ..., ...)
163 * (non-Javadoc)
164 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
165 */
166 public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
167 if (cdmFetch == null){
168 cdmFetch = CdmFetch.NO_FETCH();
169 }
170 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
171 }
172
173
174 /* (non-Javadoc)
175 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
176 */
177 public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
178 return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
179 }
180
181 /* (non-Javadoc)
182 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
183 */
184 public List<RelationshipBase> getAllRelationships(int limit, int start){
185 return dao.getAllRelationships(limit, start);
186 }
187
188 /**
189 * FIXME Candidate for harmonization
190 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
191 */
192 @Deprecated
193 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
194
195 String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
196 UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
197 OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
198 (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
199 return taxonRelTypeVocabulary;
200 }
201
202
203
204 /*
205 * (non-Javadoc)
206 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
207 */
208 @Transactional(readOnly = false)
209 public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
210
211 TaxonNameBase<?,?> synonymName = synonym.getName();
212 synonymName.removeTaxonBase(synonym);
213 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
214 taxonName.removeTaxonBase(acceptedTaxon);
215
216 synonym.setName(taxonName);
217 acceptedTaxon.setName(synonymName);
218
219 // the accepted taxon needs a new uuid because the concept has changed
220 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
221 //acceptedTaxon.setUuid(UUID.randomUUID());
222 }
223
224
225 /* (non-Javadoc)
226 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
227 */
228 //TODO correct delete handling still needs to be implemented / checked
229 @Override
230 @Transactional(readOnly = false)
231 public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
232
233 TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
234 TaxonNameBase<?,?> synonymName = synonym.getName();
235 HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
236
237 //check synonym is not homotypic
238 if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
239 String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
240 throw new HomotypicalGroupChangeException(message);
241 }
242
243 Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
244
245 SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
246 List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
247
248 for (Synonym heteroSynonym : heteroSynonyms){
249 if (synonym.equals(heteroSynonym)){
250 acceptedTaxon.removeSynonym(heteroSynonym, false);
251 }else{
252 //move synonyms in same homotypic group to new accepted taxon
253 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
254 }
255 }
256
257 //synonym.getName().removeTaxonBase(synonym);
258 //TODO correct delete handling still needs to be implemented / checked
259 if (deleteSynonym){
260 // deleteSynonym(synonym, taxon, false);
261 try {
262 this.dao.flush();
263 this.delete(synonym);
264
265 } catch (Exception e) {
266 logger.info("Can't delete old synonym from database");
267 }
268 }
269
270 return newAcceptedTaxon;
271 }
272
273
274 public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
275
276 // Get name from synonym
277 TaxonNameBase<?, ?> synonymName = synonym.getName();
278
279 // remove synonym from taxon
280 toTaxon.removeSynonym(synonym);
281
282 // Create a taxon with synonym name
283 Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
284
285 // Add taxon relation
286 fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
287
288 // since we are swapping names, we have to detach the name from the synonym completely.
289 // Otherwise the synonym will still be in the list of typified names.
290 synonym.getName().removeTaxonBase(synonym);
291
292 return fromTaxon;
293 }
294
295
296 /* (non-Javadoc)
297 * @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)
298 */
299 @Transactional(readOnly = false)
300 @Override
301 public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
302 boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
303 // Get synonym name
304 TaxonNameBase synonymName = synonym.getName();
305 HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
306
307
308 // Switch groups
309 oldHomotypicalGroup.removeTypifiedName(synonymName);
310 newHomotypicalGroup.addTypifiedName(synonymName);
311
312 //remove existing basionym relationships
313 synonymName.removeBasionyms();
314
315 //add basionym relationship
316 if (setBasionymRelationIfApplicable){
317 Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
318 for (TaxonNameBase basionym : basionyms){
319 synonymName.addBasionym(basionym);
320 }
321 }
322
323 //set synonym relationship correctly
324 // SynonymRelationship relToTaxon = null;
325 boolean relToTargetTaxonExists = false;
326 Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
327 for (SynonymRelationship rel : existingRelations){
328 Taxon acceptedTaxon = rel.getAcceptedTaxon();
329 boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
330 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
331 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
332 SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
333 rel.setType(newRelationType);
334 //TODO handle citation and microCitation
335
336 if (isTargetTaxon){
337 relToTargetTaxonExists = true;
338 }else{
339 if (removeFromOtherTaxa){
340 acceptedTaxon.removeSynonym(synonym, false);
341 }else{
342 //do nothing
343 }
344 }
345 }
346 if (targetTaxon != null && ! relToTargetTaxonExists ){
347 Taxon acceptedTaxon = targetTaxon;
348 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
349 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
350 SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
351 //TODO handle citation and microCitation
352 Reference citation = null;
353 String microCitation = null;
354 acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
355 }
356
357 }
358
359
360 /* (non-Javadoc)
361 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
362 */
363 @Override
364 @Transactional(readOnly = false)
365 public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
366 if (clazz == null){
367 clazz = TaxonBase.class;
368 }
369 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
370 }
371
372 @Autowired
373 protected void setDao(ITaxonDao dao) {
374 this.dao = dao;
375 }
376
377 /* (non-Javadoc)
378 * @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)
379 */
380 public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
381 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
382
383 List<TaxonBase> results = new ArrayList<TaxonBase>();
384 if(numberOfResults > 0) { // no point checking again
385 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
386 }
387
388 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
389 }
390
391 /* (non-Javadoc)
392 * @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)
393 */
394 public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
395 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
396
397 List<TaxonBase> results = new ArrayList<TaxonBase>();
398 if(numberOfResults > 0) { // no point checking again
399 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
400 }
401
402 return results;
403 }
404
405 /* (non-Javadoc)
406 * @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)
407 */
408 public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
409 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
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.relatedTo);
414 }
415 return results;
416 }
417
418 /* (non-Javadoc)
419 * @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)
420 */
421 public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
422 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
423
424 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
425 if(numberOfResults > 0) { // no point checking again
426 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
427 }
428 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
429 }
430
431 /* (non-Javadoc)
432 * @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)
433 */
434 public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
435 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
436
437 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
438 if(numberOfResults > 0) { // no point checking again
439 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
440 }
441 return results;
442 }
443
444 /* (non-Javadoc)
445 * @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)
446 */
447 public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
448 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
449
450 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
451 if(numberOfResults > 0) { // no point checking again
452 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
453 }
454 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
455 }
456
457 /* (non-Javadoc)
458 * @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)
459 */
460 public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
461 Integer numberOfResults = dao.countSynonyms(taxon, type);
462
463 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
464 if(numberOfResults > 0) { // no point checking again
465 results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
466 }
467
468 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
469 }
470
471 /* (non-Javadoc)
472 * @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)
473 */
474 public Pager<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
475 Integer numberOfResults = dao.countSynonyms(synonym, type);
476
477 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
478 if(numberOfResults > 0) { // no point checking again
479 results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
480 }
481
482 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
483 }
484
485 /* (non-Javadoc)
486 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
487 */
488 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
489 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
490 return t.getHomotypicSynonymsByHomotypicGroup();
491 }
492
493 /* (non-Javadoc)
494 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
495 */
496 public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
497 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
498 List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
499 List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
500 for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
501 heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
502 }
503 return heterotypicSynonymyGroups;
504 }
505
506 public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(ITaxonServiceConfigurator configurator){
507
508 List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();
509 // Class<? extends TaxonBase> clazz = null;
510 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
511 // clazz = TaxonBase.class;
512 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
513 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
514 // } else if(configurator.isDoTaxa()) {
515 // clazz = Taxon.class;
516 // //propertyPath = configurator.getTaxonPropertyPath();
517 // } else if (configurator.isDoSynonyms()) {
518 // clazz = Synonym.class;
519 // //propertyPath = configurator.getSynonymPropertyPath();
520 // }
521
522
523 result = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
524 return result;
525 }
526
527 /* (non-Javadoc)
528 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
529 */
530 public Pager<IdentifiableEntity> findTaxaAndNames(ITaxonServiceConfigurator configurator) {
531
532 List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
533 int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
534 List<TaxonBase> taxa = null;
535
536 // Taxa and synonyms
537 long numberTaxaResults = 0L;
538
539
540 List<String> propertyPath = new ArrayList<String>();
541 if(configurator.getTaxonPropertyPath() != null){
542 propertyPath.addAll(configurator.getTaxonPropertyPath());
543 }
544
545
546 if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){
547 if(configurator.getPageSize() != null){ // no point counting if we need all anyway
548 numberTaxaResults =
549 dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
550 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
551 configurator.getNamedAreas());
552 }
553
554 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
555 taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
556 configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),
557 configurator.getMatchMode(), configurator.getNamedAreas(),
558 configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
559 }
560 }
561
562 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
563
564 if(taxa != null){
565 results.addAll(taxa);
566 }
567
568 numberOfResults += numberTaxaResults;
569
570 // Names without taxa
571 if (configurator.isDoNamesWithoutTaxa()) {
572 int numberNameResults = 0;
573
574 List<? extends TaxonNameBase<?,?>> names =
575 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
576 configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
577 if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
578 if (names.size() > 0) {
579 for (TaxonNameBase<?,?> taxonName : names) {
580 if (taxonName.getTaxonBases().size() == 0) {
581 results.add(taxonName);
582 numberNameResults++;
583 }
584 }
585 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
586 numberOfResults += numberNameResults;
587 }
588 }
589
590 // Taxa from common names
591
592 if (configurator.isDoTaxaByCommonNames()) {
593 taxa = new ArrayList<TaxonBase>();
594 numberTaxaResults = 0;
595 if(configurator.getPageSize() != null){// no point counting if we need all anyway
596 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
597 }
598 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
599 List<Object[]> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
600 for( Object[] entry : commonNameResults ) {
601 taxa.add((TaxonBase) entry[0]);
602 }
603 }
604 if(taxa != null){
605 results.addAll(taxa);
606 }
607 numberOfResults += numberTaxaResults;
608
609 }
610
611 return new DefaultPagerImpl<IdentifiableEntity>
612 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
613 }
614
615 public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
616 return dao.getUuidAndTitleCache();
617 }
618
619 /* (non-Javadoc)
620 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
621 */
622 public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
623 List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
624 taxon = (Taxon)dao.load(taxon.getUuid());
625 Set<TaxonDescription> descriptions = taxon.getDescriptions();
626 for (TaxonDescription taxDesc: descriptions){
627 Set<DescriptionElementBase> elements = taxDesc.getElements();
628 for (DescriptionElementBase descElem: elements){
629 for(Media media : descElem.getMedia()){
630
631 //find the best matching representation
632 medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
633
634 }
635 }
636 }
637 return medRep;
638 }
639
640 /* (non-Javadoc)
641 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
642 */
643 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
644 return this.dao.findById(listOfIDs);
645 }
646
647 /* (non-Javadoc)
648 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
649 */
650 public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
651 return this.dao.findByUuid(uuid, null ,propertyPaths);
652 }
653
654 /* (non-Javadoc)
655 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
656 */
657 public int countAllRelationships() {
658 return this.dao.countAllRelationships();
659 }
660
661
662
663
664 /* (non-Javadoc)
665 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
666 */
667 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
668 return this.dao.findIdenticalTaxonNames(propertyPath);
669 }
670
671
672 /* (non-Javadoc)
673 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
674 */
675 @Override
676 public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config) throws ReferencedObjectUndeletableException {
677 if (config == null){
678 config = new TaxonDeletionConfigurator();
679 }
680
681 // TaxonNode
682 if (! config.isDeleteTaxonNodes()){
683 if (taxon.getTaxonNodes().size() > 0){
684 String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
685 throw new ReferencedObjectUndeletableException(message);
686 }
687 }
688
689
690 // SynonymRelationShip
691 if (config.isDeleteSynonymRelations()){
692 boolean removeSynonymNameFromHomotypicalGroup = false;
693 for (SynonymRelationship synRel : taxon.getSynonymRelations()){
694 Synonym synonym = synRel.getSynonym();
695 taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);
696 if (config.isDeleteSynonymsIfPossible()){
697 //TODO which value
698 boolean newHomotypicGroupIfNeeded = true;
699 deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);
700 }else{
701 deleteSynonymRelationships(synonym, taxon);
702 }
703 }
704 }
705
706 // TaxonRelationship
707 if (! config.isDeleteTaxonRelationships()){
708 if (taxon.getTaxonRelations().size() > 0){
709 String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";
710 throw new ReferencedObjectUndeletableException(message);
711 }
712 }
713
714
715 // TaxonDescription
716 Set<TaxonDescription> descriptions = taxon.getDescriptions();
717
718 for (TaxonDescription desc: descriptions){
719 if (config.isDeleteDescriptions()){
720 //TODO use description delete configurator ?
721 //FIXME check if description is ALWAYS deletable
722 descriptionService.delete(desc);
723 }else{
724 if (desc.getDescribedSpecimenOrObservations().size()>0){
725 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
726 " which also describes specimens or abservations";
727 throw new ReferencedObjectUndeletableException(message);
728 }
729 }
730 }
731
732
733 //check references with only reverse mapping
734 Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);
735 for (CdmBase referencingObject : referencingObjects){
736 //IIdentificationKeys (Media, Polytomous, MultiAccess)
737 if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){
738 String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
739 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnitBase.class).getTitleCache());
740 throw new ReferencedObjectUndeletableException(message);
741 }
742
743
744 //PolytomousKeyNode
745 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
746 String message = "Taxon can't be deleted as it is used in polytomous key node";
747 throw new ReferencedObjectUndeletableException(message);
748 }
749
750 //TaxonInteraction
751 if (referencingObject.isInstanceOf(TaxonInteraction.class)){
752 String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
753 throw new ReferencedObjectUndeletableException(message);
754 }
755 }
756
757
758 //TaxonNameBase
759 if (config.isDeleteNameIfPossible()){
760 try {
761 nameService.delete(taxon.getName(), config.getNameDeletionConfig());
762 } catch (ReferencedObjectUndeletableException e) {
763 //do nothing
764 if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}
765 }
766 }
767
768 }
769
770 /* (non-Javadoc)
771 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
772 */
773 @Transactional(readOnly = false)
774 @Override
775 public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
776 if (synonym == null){
777 return;
778 }
779 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
780
781 //remove synonymRelationship
782 Set<Taxon> taxonSet = new HashSet<Taxon>();
783 if (taxon != null){
784 taxonSet.add(taxon);
785 }else{
786 taxonSet.addAll(synonym.getAcceptedTaxa());
787 }
788 for (Taxon relatedTaxon : taxonSet){
789 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
790 relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
791 }
792 this.saveOrUpdate(synonym);
793
794 //TODO remove name from homotypical group?
795
796 //remove synonym (if necessary)
797 if (synonym.getSynonymRelations().isEmpty()){
798 TaxonNameBase<?,?> name = synonym.getName();
799 synonym.setName(null);
800 dao.delete(synonym);
801
802 //remove name if possible (and required)
803 if (name != null && removeNameIfPossible){
804 try{
805 nameService.delete(name, new NameDeletionConfigurator());
806 }catch (DataChangeNoRollbackException ex){
807 if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");
808 }
809 }
810 }
811 }
812
813
814 /* (non-Javadoc)
815 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
816 */
817 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
818
819 return this.dao.findIdenticalNamesNew(propertyPath);
820 }
821
822 /* (non-Javadoc)
823 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
824 */
825 public String getPhylumName(TaxonNameBase name){
826 return this.dao.getPhylumName(name);
827 }
828
829 /* (non-Javadoc)
830 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
831 */
832 public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {
833 return dao.deleteSynonymRelationships(syn, taxon);
834 }
835
836 /* (non-Javadoc)
837 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
838 */
839 public long deleteSynonymRelationships(Synonym syn) {
840 return dao.deleteSynonymRelationships(syn, null);
841 }
842
843
844 /* (non-Javadoc)
845 * @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)
846 */
847 public List<SynonymRelationship> listSynonymRelationships(
848 TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
849 List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
850 Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
851
852 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
853 if(numberOfResults > 0) { // no point checking again
854 results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
855 }
856 return results;
857 }
858
859 /* (non-Javadoc)
860 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
861 */
862 @Override
863 public Taxon findBestMatchingTaxon(String taxonName) {
864 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
865 config.setTaxonNameTitle(taxonName);
866 return findBestMatchingTaxon(config);
867 }
868
869
870
871 @Override
872 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
873
874 Taxon bestCandidate = null;
875 try{
876 // 1. search for acceptet taxa
877 List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
878 boolean bestCandidateMatchesSecUuid = false;
879 boolean bestCandidateIsInClassification = false;
880 int countEqualCandidates = 0;
881 for(TaxonBase taxonBaseCandidate : taxonList){
882 if(taxonBaseCandidate instanceof Taxon){
883 Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
884 boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
885 if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
886 continue;
887 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
888 bestCandidate = newCanditate;
889 countEqualCandidates = 1;
890 bestCandidateMatchesSecUuid = true;
891 continue;
892 }
893
894 boolean newCandidateInClassification = isInClassification(newCanditate, config);
895 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
896 continue;
897 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
898 bestCandidate = newCanditate;
899 countEqualCandidates = 1;
900 bestCandidateIsInClassification = true;
901 continue;
902 }
903 if (bestCandidate == null){
904 bestCandidate = newCanditate;
905 countEqualCandidates = 1;
906 continue;
907 }
908
909 }else{ //not Taxon.class
910 continue;
911 }
912 countEqualCandidates++;
913
914 }
915 if (bestCandidate != null){
916 if(countEqualCandidates > 1){
917 logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
918 return bestCandidate;
919 } else {
920 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
921 return bestCandidate;
922 }
923 }
924
925
926 // 2. search for synonyms
927 if (config.isIncludeSynonyms()){
928 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
929 for(TaxonBase taxonBase : synonymList){
930 if(taxonBase instanceof Synonym){
931 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
932 Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
933 if(!acceptetdCandidates.isEmpty()){
934 bestCandidate = acceptetdCandidates.iterator().next();
935 if(acceptetdCandidates.size() == 1){
936 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
937 return bestCandidate;
938 } else {
939 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
940 return bestCandidate;
941 }
942 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
943 }
944 }
945 }
946 }
947
948 } catch (Exception e){
949 logger.error(e);
950 }
951
952 return bestCandidate;
953 }
954
955 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
956 UUID configClassificationUuid = config.getClassificationUuid();
957 if (configClassificationUuid == null){
958 return false;
959 }
960 for (TaxonNode node : taxon.getTaxonNodes()){
961 UUID classUuid = node.getClassification().getUuid();
962 if (configClassificationUuid.equals(classUuid)){
963 return true;
964 }
965 }
966 return false;
967 }
968
969 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
970 UUID configSecUuid = config.getSecUuid();
971 if (configSecUuid == null){
972 return false;
973 }
974 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
975 return configSecUuid.equals(taxonSecUuid);
976 }
977
978 /* (non-Javadoc)
979 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
980 */
981 @Override
982 public Synonym findBestMatchingSynonym(String taxonName) {
983 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
984 if(! synonymList.isEmpty()){
985 Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
986 if(synonymList.size() == 1){
987 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
988 return result;
989 } else {
990 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
991 return result;
992 }
993 }
994 return null;
995 }
996
997
998 /* (non-Javadoc)
999 * @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)
1000 */
1001 @Override
1002 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
1003 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
1004
1005 Synonym synonym = oldSynonymRelation.getSynonym();
1006 Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
1007 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1008 TaxonNameBase<?,?> synonymName = synonym.getName();
1009 TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
1010 //set default relationship type
1011 if (newSynonymRelationshipType == null){
1012 newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
1013 }
1014 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
1015
1016 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
1017 int hgSize = homotypicGroup.getTypifiedNames().size();
1018 boolean isSingleInGroup = !(hgSize > 1);
1019
1020 if (! isSingleInGroup){
1021 boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
1022 boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
1023 if (isHomotypicToAccepted){
1024 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.";
1025 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
1026 message = String.format(message, homotypicRelatives);
1027 throw new HomotypicalGroupChangeException(message);
1028 }
1029 if (! moveHomotypicGroup){
1030 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.";
1031 throw new HomotypicalGroupChangeException(message);
1032 }
1033 }else{
1034 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
1035 }
1036 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1037
1038 SynonymRelationship result = null;
1039 //move all synonyms to new taxon
1040 List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
1041 for (Synonym syn: homotypicSynonyms){
1042 Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
1043 for (SynonymRelationship synRelation : synRelations){
1044 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
1045 Reference<?> newReference = reference;
1046 if (newReference == null && keepReference){
1047 newReference = synRelation.getCitation();
1048 }
1049 String newRefDetail = referenceDetail;
1050 if (newRefDetail == null && keepReference){
1051 newRefDetail = synRelation.getCitationMicroReference();
1052 }
1053 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
1054 fromTaxon.removeSynonymRelation(synRelation, false);
1055 //
1056 //change homotypic group of synonym if relType is 'homotypic'
1057 // if (newRelTypeIsHomotypic){
1058 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1059 // }
1060 //set result
1061 if (synRelation.equals(oldSynonymRelation)){
1062 result = newSynRelation;
1063 }
1064 }
1065 }
1066
1067 }
1068 saveOrUpdate(newTaxon);
1069 //Assert that there is a result
1070 if (result == null){
1071 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
1072 throw new IllegalStateException(message);
1073 }
1074 return result;
1075 }
1076
1077 /* (non-Javadoc)
1078 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1079 */
1080 @Override
1081 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1082 return dao.getUuidAndTitleCacheTaxon();
1083 }
1084
1085 /* (non-Javadoc)
1086 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1087 */
1088 @Override
1089 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
1090 return dao.getUuidAndTitleCacheSynonym();
1091 }
1092
1093 @Override
1094 public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
1095 Class<? extends DescriptionElementBase> clazz, String queryString,
1096 Classification classification, List<Language> languages, Integer pageSize,
1097 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1098
1099 Class<? extends DescriptionElementBase> directorySelectClass = DescriptionElementBase.class;
1100 if(clazz != null){
1101 directorySelectClass = clazz;
1102 }
1103
1104 // ---- search criteria
1105 StringBuilder luceneQueryTemplate = new StringBuilder();
1106 luceneQueryTemplate.append("+(");
1107 luceneQueryTemplate.append("titleCache:%1$s ");
1108 // common name
1109 if(languages == null || languages.size() == 0){
1110 luceneQueryTemplate.append("name:%1$s ");
1111 } else {
1112 luceneQueryTemplate.append("(+name:%1$s ");
1113 for(Language lang : languages){
1114 luceneQueryTemplate.append(" +language.uuid:" + lang.getUuid().toString());
1115 }
1116 luceneQueryTemplate.append(")");
1117 }
1118 // text field from TextData
1119 appendLocalizedFieldQuery("text", languages, luceneQueryTemplate).append(" ");
1120 // state field from CategoricalData
1121 appendLocalizedFieldQuery("states.state.representation", languages, luceneQueryTemplate).append(" ");
1122 // state field from CategoricalData
1123 appendLocalizedFieldQuery("states.modifyingText", languages, luceneQueryTemplate).append(" ");
1124 luceneQueryTemplate.append(") ");
1125
1126 if(classification != null){
1127 luceneQueryTemplate.append("+inDescription.taxon.taxonNodes.classification.id:").append(classification.getId()).append(" ");
1128 }
1129 // the description must be associated with a taxon
1130 // TODO open range queries [0 TO *] not working in the current version of lucene (https://issues.apache.org/jira/browse/LUCENE-995)
1131 // so we are using integer maximum as workaround
1132 luceneQueryTemplate.append("+inDescription.taxon.id:[0 TO " + Integer.MAX_VALUE + "] ");
1133
1134 String luceneQueryStr = String.format(luceneQueryTemplate.toString(), queryString);
1135
1136 // ---- execute criteria
1137 LuceneSearch luceneSearch = new LuceneSearch(getSession(), directorySelectClass);
1138
1139 TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQueryStr, clazz, pageSize, pageNumber);
1140
1141 // initialize taxa
1142 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id", propertyPaths);
1143
1144 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);
1145
1146 }
1147
1148 /**
1149 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1150 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1151 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1152 *
1153 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1154 * or {@link MultilanguageTextFieldBridge }
1155 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1156 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1157 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1158 *
1159 * TODO move to utiliy class !!!!!!!!
1160 */
1161 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1162
1163 if(stringBuilder == null){
1164 stringBuilder = new StringBuilder();
1165 }
1166 if(languages == null || languages.size() == 0){
1167 stringBuilder.append(name + ".ALL:%1$s ");
1168 } else {
1169 for(Language lang : languages){
1170 stringBuilder.append(name + "." + lang.getUuid().toString() + ":%1$s ");
1171 }
1172 }
1173 return stringBuilder;
1174 }
1175
1176 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1177 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1178 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1179
1180 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1181
1182
1183 UUID uuid= taxon.getName().getUuid();
1184 ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);
1185 String epithetOfTaxon = null;
1186 String infragenericEpithetOfTaxon = null;
1187 String infraspecificEpithetOfTaxon = null;
1188 if (taxonName.isSpecies()){
1189 epithetOfTaxon= taxonName.getSpecificEpithet();
1190 } else if (taxonName.isInfraGeneric()){
1191 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1192 } else if (taxonName.isInfraSpecific()){
1193 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1194 }
1195 String genusOfTaxon = taxonName.getGenusOrUninomial();
1196 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1197 List<String> taxonNames = new ArrayList<String>();
1198
1199 for (TaxonNode node: nodes){
1200 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1201 // List<String> synonymsEpithet = new ArrayList<String>();
1202
1203 if (node.getClassification().equals(classification)){
1204 if (!node.isTopmostNode()){
1205 TaxonNode parent = (TaxonNode)node.getParent();
1206 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1207 TaxonNameBase parentName = parent.getTaxon().getName();
1208 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1209 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1210 Rank rankOfTaxon = taxonName.getRank();
1211
1212
1213 //create inferred synonyms for species, subspecies
1214 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1215
1216 Synonym inferredEpithet = null;
1217 Synonym inferredGenus = null;
1218 Synonym potentialCombination = null;
1219
1220 List<String> propertyPaths = new ArrayList<String>();
1221 propertyPaths.add("synonym");
1222 propertyPaths.add("synonym.name");
1223 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1224 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1225
1226 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1227 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1228
1229 List<TaxonRelationship> taxonRelListParent = null;
1230 List<TaxonRelationship> taxonRelListTaxon = null;
1231 if (doWithMisappliedNames){
1232 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1233 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1234 }
1235
1236
1237 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1238 Set<String> genusNames = new HashSet<String>();
1239
1240 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1241 Synonym syn = synonymRelationOfParent.getSynonym();
1242
1243 inferredEpithet = createInferredEpithets(taxon,
1244 zooHashMap, taxonName, epithetOfTaxon,
1245 infragenericEpithetOfTaxon,
1246 infraspecificEpithetOfTaxon,
1247 taxonNames, parentName,
1248 syn);
1249
1250
1251 inferredSynonyms.add(inferredEpithet);
1252 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1253 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1254 }
1255
1256 if (doWithMisappliedNames){
1257
1258 for (TaxonRelationship taxonRelationship: taxonRelListParent){
1259 Taxon misappliedName = taxonRelationship.getFromTaxon();
1260
1261 inferredEpithet = createInferredEpithets(taxon,
1262 zooHashMap, taxonName, epithetOfTaxon,
1263 infragenericEpithetOfTaxon,
1264 infraspecificEpithetOfTaxon,
1265 taxonNames, parentName,
1266 misappliedName);
1267
1268 inferredSynonyms.add(inferredEpithet);
1269 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1270 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1271 }
1272 }
1273
1274 if (!taxonNames.isEmpty()){
1275 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1276 ZoologicalName name;
1277 if (!synNotInCDM.isEmpty()){
1278 inferredSynonymsToBeRemoved.clear();
1279
1280 for (Synonym syn :inferredSynonyms){
1281 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1282 if (!synNotInCDM.contains(name.getNameCache())){
1283 inferredSynonymsToBeRemoved.add(syn);
1284 }
1285 }
1286
1287 // Remove identified Synonyms from inferredSynonyms
1288 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1289 inferredSynonyms.remove(synonym);
1290 }
1291 }
1292 }
1293
1294 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1295
1296
1297 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1298 TaxonNameBase synName;
1299 ZoologicalName inferredSynName;
1300
1301 Synonym syn = synonymRelationOfTaxon.getSynonym();
1302 inferredGenus = createInferredGenus(taxon,
1303 zooHashMap, taxonName, epithetOfTaxon,
1304 genusOfTaxon, taxonNames, zooParentName, syn);
1305
1306 inferredSynonyms.add(inferredGenus);
1307 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1308 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1309
1310
1311 }
1312
1313 if (doWithMisappliedNames){
1314
1315 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1316 Taxon misappliedName = taxonRelationship.getFromTaxon();
1317 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
1318
1319 inferredSynonyms.add(inferredGenus);
1320 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1321 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1322 }
1323 }
1324
1325
1326 if (!taxonNames.isEmpty()){
1327 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1328 ZoologicalName name;
1329 if (!synNotInCDM.isEmpty()){
1330 inferredSynonymsToBeRemoved.clear();
1331
1332 for (Synonym syn :inferredSynonyms){
1333 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1334 if (!synNotInCDM.contains(name.getNameCache())){
1335 inferredSynonymsToBeRemoved.add(syn);
1336 }
1337 }
1338
1339 // Remove identified Synonyms from inferredSynonyms
1340 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1341 inferredSynonyms.remove(synonym);
1342 }
1343 }
1344 }
1345
1346 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1347
1348 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1349 ZoologicalName inferredSynName;
1350 //for all synonyms of the parent...
1351 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1352 TaxonNameBase synName;
1353 Synonym synParent = synonymRelationOfParent.getSynonym();
1354 synName = synParent.getName();
1355
1356 HibernateProxyHelper.deproxy(synParent);
1357
1358 // Set the sourceReference
1359 sourceReference = synParent.getSec();
1360
1361 // Determine the idInSource
1362 String idInSourceParent = getIdInSource(synParent);
1363
1364 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1365 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1366 String synParentInfragenericName = null;
1367 String synParentSpecificEpithet = null;
1368
1369 if (parentSynZooName.isInfraGeneric()){
1370 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1371 }
1372 if (parentSynZooName.isSpecies()){
1373 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1374 }
1375
1376 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1377 synonymsGenus.put(synGenusName, idInSource);
1378 }*/
1379
1380 //for all synonyms of the taxon
1381
1382 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1383
1384 Synonym syn = synonymRelationOfTaxon.getSynonym();
1385 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1386 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1387 synParentGenus,
1388 synParentInfragenericName,
1389 synParentSpecificEpithet, syn, zooHashMap);
1390
1391 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1392 inferredSynonyms.add(potentialCombination);
1393 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1394 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1395
1396 }
1397
1398
1399 }
1400
1401 if (doWithMisappliedNames){
1402
1403 for (TaxonRelationship parentRelationship: taxonRelListParent){
1404
1405 TaxonNameBase misappliedParentName;
1406
1407 Taxon misappliedParent = parentRelationship.getFromTaxon();
1408 misappliedParentName = misappliedParent.getName();
1409
1410 HibernateProxyHelper.deproxy(misappliedParent);
1411
1412 // Set the sourceReference
1413 sourceReference = misappliedParent.getSec();
1414
1415 // Determine the idInSource
1416 String idInSourceParent = getIdInSource(misappliedParent);
1417
1418 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1419 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1420 String synParentInfragenericName = null;
1421 String synParentSpecificEpithet = null;
1422
1423 if (parentSynZooName.isInfraGeneric()){
1424 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1425 }
1426 if (parentSynZooName.isSpecies()){
1427 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1428 }
1429
1430
1431 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1432 Taxon misappliedName = taxonRelationship.getFromTaxon();
1433 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1434 potentialCombination = createPotentialCombination(
1435 idInSourceParent, parentSynZooName, zooMisappliedName,
1436 synParentGenus,
1437 synParentInfragenericName,
1438 synParentSpecificEpithet, misappliedName, zooHashMap);
1439
1440
1441 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1442 inferredSynonyms.add(potentialCombination);
1443 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1444 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1445 }
1446 }
1447 }
1448
1449 if (!taxonNames.isEmpty()){
1450 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1451 ZoologicalName name;
1452 if (!synNotInCDM.isEmpty()){
1453 inferredSynonymsToBeRemoved.clear();
1454 for (Synonym syn :inferredSynonyms){
1455 try{
1456 name = (ZoologicalName) syn.getName();
1457 }catch (ClassCastException e){
1458 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1459 }
1460 if (!synNotInCDM.contains(name.getNameCache())){
1461 inferredSynonymsToBeRemoved.add(syn);
1462 }
1463 }
1464 // Remove identified Synonyms from inferredSynonyms
1465 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1466 inferredSynonyms.remove(synonym);
1467 }
1468 }
1469 }
1470 }
1471 }else {
1472 logger.info("The synonymrelationship type is not defined.");
1473 return inferredSynonyms;
1474 }
1475 }
1476 }
1477
1478 }
1479
1480 return inferredSynonyms;
1481 }
1482
1483 private Synonym createPotentialCombination(String idInSourceParent,
1484 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
1485 String synParentInfragenericName, String synParentSpecificEpithet,
1486 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1487 Synonym potentialCombination;
1488 Reference sourceReference;
1489 ZoologicalName inferredSynName;
1490 HibernateProxyHelper.deproxy(syn);
1491
1492 // Set sourceReference
1493 sourceReference = syn.getSec();
1494 if (sourceReference == null){
1495 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1496 //TODO:Remove
1497 if (!parentSynZooName.getTaxa().isEmpty()){
1498 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
1499
1500 sourceReference = taxon.getSec();
1501 }
1502 }
1503 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1504
1505 String synTaxonInfraSpecificName= null;
1506
1507 if (parentSynZooName.isSpecies()){
1508 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1509 }
1510
1511 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1512 synonymsEpithet.add(epithetName);
1513 }*/
1514
1515 //create potential combinations...
1516 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1517
1518 inferredSynName.setGenusOrUninomial(synParentGenus);
1519 if (zooSynName.isSpecies()){
1520 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1521 if (parentSynZooName.isInfraGeneric()){
1522 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1523 }
1524 }
1525 if (zooSynName.isInfraSpecific()){
1526 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1527 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1528 }
1529 if (parentSynZooName.isInfraGeneric()){
1530 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1531 }
1532
1533
1534 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1535
1536 // Set the sourceReference
1537 potentialCombination.setSec(sourceReference);
1538
1539
1540 // Determine the idInSource
1541 String idInSourceSyn= getIdInSource(syn);
1542
1543 if (idInSourceParent != null && idInSourceSyn != null) {
1544 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1545 inferredSynName.addSource(originalSource);
1546 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1547 potentialCombination.addSource(originalSource);
1548 }
1549
1550 inferredSynName.generateTitle();
1551
1552 return potentialCombination;
1553 }
1554
1555 private Synonym createInferredGenus(Taxon taxon,
1556 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1557 String epithetOfTaxon, String genusOfTaxon,
1558 List<String> taxonNames, ZoologicalName zooParentName,
1559 TaxonBase syn) {
1560
1561 Synonym inferredGenus;
1562 TaxonNameBase synName;
1563 ZoologicalName inferredSynName;
1564 synName =syn.getName();
1565 HibernateProxyHelper.deproxy(syn);
1566
1567 // Determine the idInSource
1568 String idInSourceSyn = getIdInSource(syn);
1569 String idInSourceTaxon = getIdInSource(taxon);
1570 // Determine the sourceReference
1571 Reference sourceReference = syn.getSec();
1572
1573 //logger.warn(sourceReference.getTitleCache());
1574
1575 synName = syn.getName();
1576 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1577 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1578 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1579 synonymsEpithet.add(synSpeciesEpithetName);
1580 }*/
1581
1582 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1583 //TODO:differ between parent is genus 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...
1584
1585
1586 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1587 if (zooParentName.isInfraGeneric()){
1588 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1589 }
1590
1591 if (taxonName.isSpecies()){
1592 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1593 }
1594 if (taxonName.isInfraSpecific()){
1595 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1596 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1597 }
1598
1599
1600 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1601
1602 // Set the sourceReference
1603 inferredGenus.setSec(sourceReference);
1604
1605 // Add the original source
1606 if (idInSourceSyn != null && idInSourceTaxon != null) {
1607 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1608 inferredGenus.addSource(originalSource);
1609
1610 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1611 inferredSynName.addSource(originalSource);
1612 originalSource = null;
1613
1614 }else{
1615 logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
1616 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1617 inferredGenus.addSource(originalSource);
1618
1619 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1620 inferredSynName.addSource(originalSource);
1621 originalSource = null;
1622 }
1623
1624 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
1625
1626 inferredSynName.generateTitle();
1627
1628
1629 return inferredGenus;
1630 }
1631
1632 private Synonym createInferredEpithets(Taxon taxon,
1633 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1634 String epithetOfTaxon, String infragenericEpithetOfTaxon,
1635 String infraspecificEpithetOfTaxon, List<String> taxonNames,
1636 TaxonNameBase parentName, TaxonBase syn) {
1637
1638 Synonym inferredEpithet;
1639 TaxonNameBase synName;
1640 ZoologicalName inferredSynName;
1641 HibernateProxyHelper.deproxy(syn);
1642
1643 // Determine the idInSource
1644 String idInSourceSyn = getIdInSource(syn);
1645 String idInSourceTaxon = getIdInSource(taxon);
1646 // Determine the sourceReference
1647 Reference sourceReference = syn.getSec();
1648
1649 if (sourceReference == null){
1650 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1651 //TODO:Remove
1652 System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
1653 sourceReference = taxon.getSec();
1654 }
1655
1656 synName = syn.getName();
1657 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
1658 String synGenusName = zooSynName.getGenusOrUninomial();
1659 String synInfraGenericEpithet = null;
1660 String synSpecificEpithet = null;
1661
1662 if (zooSynName.getInfraGenericEpithet() != null){
1663 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
1664 }
1665
1666 if (zooSynName.isInfraSpecific()){
1667 synSpecificEpithet = zooSynName.getSpecificEpithet();
1668 }
1669
1670 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1671 synonymsGenus.put(synGenusName, idInSource);
1672 }*/
1673
1674 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1675
1676 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1677 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
1678 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
1679 }
1680 inferredSynName.setGenusOrUninomial(synGenusName);
1681
1682 if (parentName.isInfraGeneric()){
1683 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
1684 }
1685 if (taxonName.isSpecies()){
1686 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1687 }else if (taxonName.isInfraSpecific()){
1688 inferredSynName.setSpecificEpithet(synSpecificEpithet);
1689 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
1690 }
1691
1692 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1693
1694 // Set the sourceReference
1695 inferredEpithet.setSec(sourceReference);
1696
1697 /* Add the original source
1698 if (idInSource != null) {
1699 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1700
1701 // Add the citation
1702 Reference citation = getCitation(syn);
1703 if (citation != null) {
1704 originalSource.setCitation(citation);
1705 inferredEpithet.addSource(originalSource);
1706 }
1707 }*/
1708 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
1709
1710 IdentifiableSource originalSource;
1711 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1712
1713 inferredEpithet.addSource(originalSource);
1714
1715 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1716
1717 inferredSynName.addSource(originalSource);
1718
1719
1720
1721 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
1722
1723 inferredSynName.generateTitle();
1724 return inferredEpithet;
1725 }
1726
1727 /**
1728 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1729 * Very likely only useful for createInferredSynonyms().
1730 * @param uuid
1731 * @param zooHashMap
1732 * @return
1733 */
1734 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
1735 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
1736 if (taxonName == null) {
1737 taxonName = zooHashMap.get(uuid);
1738 }
1739 return taxonName;
1740 }
1741
1742 /**
1743 * Returns the idInSource for a given Synonym.
1744 * @param syn
1745 */
1746 private String getIdInSource(TaxonBase taxonBase) {
1747 String idInSource = null;
1748 Set<IdentifiableSource> sources = taxonBase.getSources();
1749 if (sources.size() == 1) {
1750 IdentifiableSource source = sources.iterator().next();
1751 if (source != null) {
1752 idInSource = source.getIdInSource();
1753 }
1754 } else if (sources.size() > 1) {
1755 int count = 1;
1756 idInSource = "";
1757 for (IdentifiableSource source : sources) {
1758 idInSource += source.getIdInSource();
1759 if (count < sources.size()) {
1760 idInSource += "; ";
1761 }
1762 count++;
1763 }
1764 } else if (sources.size() == 0){
1765 logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
1766 }
1767
1768
1769 return idInSource;
1770 }
1771
1772
1773 /**
1774 * Returns the citation for a given Synonym.
1775 * @param syn
1776 */
1777 private Reference getCitation(Synonym syn) {
1778 Reference citation = null;
1779 Set<IdentifiableSource> sources = syn.getSources();
1780 if (sources.size() == 1) {
1781 IdentifiableSource source = sources.iterator().next();
1782 if (source != null) {
1783 citation = source.getCitation();
1784 }
1785 } else if (sources.size() > 1) {
1786 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
1787 }
1788
1789 return citation;
1790 }
1791
1792 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
1793 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1794
1795 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
1796 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
1797 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
1798
1799 return inferredSynonyms;
1800 }
1801
1802
1803
1804
1805 }