[part1/2] more tests for #476: Implement free-text search methods for TaxonBase and...
[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.DefinedTermBaseFieldBridge;
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 List<Language> languages, Integer pageSize, Integer pageNumber,
1097 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 StringBuilder luceneQueryTemplate = new StringBuilder();
1105
1106 luceneQueryTemplate.append("titleCache:%1$s ");
1107 // common name
1108 if(languages == null || languages.size() == 0){
1109 luceneQueryTemplate.append("name:%1$s ");
1110 } else {
1111 luceneQueryTemplate.append("(name:%1$s AND (");
1112 for(Language lang : languages){
1113 luceneQueryTemplate.append(" language.uuid:" + lang.getUuid().toString());
1114 }
1115 luceneQueryTemplate.append("))");
1116 }
1117 // text field from TextData
1118 appendLocalizedFieldQuery("text", languages, luceneQueryTemplate).append(" ");
1119 // state field from CategoricalData
1120 appendLocalizedFieldQuery("states.state.representation", languages, luceneQueryTemplate).append(" ");
1121 // state field from CategoricalData
1122 appendLocalizedFieldQuery("states.modifyingText", languages, luceneQueryTemplate).append(" ");
1123
1124 // String luceneQueryTemplate = typeSelect + "( titleCache:%1$s OR " + languageSelection + "OR name:%1$s )";
1125 String luceneQuery = String.format(luceneQueryTemplate.toString(), queryString);
1126
1127 LuceneSearch luceneSearch = new LuceneSearch(getSession(), directorySelectClass);
1128 TopDocs topDocsResultSet = luceneSearch.executeSearch(luceneQuery, clazz);
1129 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSetFromIds(luceneSearch, topDocsResultSet, dao, "inDescription.taxon.id");
1130
1131 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);
1132
1133 }
1134
1135 /**
1136 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseFieldBridge}
1137 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1138 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1139 *
1140 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseFieldBridge}
1141 * or {@link MultilanguageTextFieldBridge }
1142 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1143 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1144 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1145 *
1146 * TODO move to utiliy class !!!!!!!!
1147 */
1148 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1149
1150 if(stringBuilder == null){
1151 stringBuilder = new StringBuilder();
1152 }
1153 if(languages == null || languages.size() == 0){
1154 stringBuilder.append(name + ".ALL:%1$s ");
1155 } else {
1156 for(Language lang : languages){
1157 stringBuilder.append(name + "." + lang.getUuid().toString() + ":%1$s ");
1158 }
1159 }
1160 return stringBuilder;
1161 }
1162
1163 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1164 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1165 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1166
1167 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1168
1169
1170 UUID uuid= taxon.getName().getUuid();
1171 ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);
1172 String epithetOfTaxon = null;
1173 String infragenericEpithetOfTaxon = null;
1174 String infraspecificEpithetOfTaxon = null;
1175 if (taxonName.isSpecies()){
1176 epithetOfTaxon= taxonName.getSpecificEpithet();
1177 } else if (taxonName.isInfraGeneric()){
1178 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1179 } else if (taxonName.isInfraSpecific()){
1180 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1181 }
1182 String genusOfTaxon = taxonName.getGenusOrUninomial();
1183 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1184 List<String> taxonNames = new ArrayList<String>();
1185
1186 for (TaxonNode node: nodes){
1187 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1188 // List<String> synonymsEpithet = new ArrayList<String>();
1189
1190 if (node.getClassification().equals(classification)){
1191 if (!node.isTopmostNode()){
1192 TaxonNode parent = (TaxonNode)node.getParent();
1193 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1194 TaxonNameBase parentName = parent.getTaxon().getName();
1195 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1196 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1197 Rank rankOfTaxon = taxonName.getRank();
1198
1199
1200 //create inferred synonyms for species, subspecies
1201 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1202
1203 Synonym inferredEpithet = null;
1204 Synonym inferredGenus = null;
1205 Synonym potentialCombination = null;
1206
1207 List<String> propertyPaths = new ArrayList<String>();
1208 propertyPaths.add("synonym");
1209 propertyPaths.add("synonym.name");
1210 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1211 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1212
1213 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1214 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1215
1216 List<TaxonRelationship> taxonRelListParent = null;
1217 List<TaxonRelationship> taxonRelListTaxon = null;
1218 if (doWithMisappliedNames){
1219 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1220 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1221 }
1222
1223
1224 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1225 Set<String> genusNames = new HashSet<String>();
1226
1227 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1228 Synonym syn = synonymRelationOfParent.getSynonym();
1229
1230 inferredEpithet = createInferredEpithets(taxon,
1231 zooHashMap, taxonName, epithetOfTaxon,
1232 infragenericEpithetOfTaxon,
1233 infraspecificEpithetOfTaxon,
1234 taxonNames, parentName,
1235 syn);
1236
1237
1238 inferredSynonyms.add(inferredEpithet);
1239 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1240 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1241 }
1242
1243 if (doWithMisappliedNames){
1244
1245 for (TaxonRelationship taxonRelationship: taxonRelListParent){
1246 Taxon misappliedName = taxonRelationship.getFromTaxon();
1247
1248 inferredEpithet = createInferredEpithets(taxon,
1249 zooHashMap, taxonName, epithetOfTaxon,
1250 infragenericEpithetOfTaxon,
1251 infraspecificEpithetOfTaxon,
1252 taxonNames, parentName,
1253 misappliedName);
1254
1255 inferredSynonyms.add(inferredEpithet);
1256 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1257 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1258 }
1259 }
1260
1261 if (!taxonNames.isEmpty()){
1262 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1263 ZoologicalName name;
1264 if (!synNotInCDM.isEmpty()){
1265 inferredSynonymsToBeRemoved.clear();
1266
1267 for (Synonym syn :inferredSynonyms){
1268 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1269 if (!synNotInCDM.contains(name.getNameCache())){
1270 inferredSynonymsToBeRemoved.add(syn);
1271 }
1272 }
1273
1274 // Remove identified Synonyms from inferredSynonyms
1275 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1276 inferredSynonyms.remove(synonym);
1277 }
1278 }
1279 }
1280
1281 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1282
1283
1284 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1285 TaxonNameBase synName;
1286 ZoologicalName inferredSynName;
1287
1288 Synonym syn = synonymRelationOfTaxon.getSynonym();
1289 inferredGenus = createInferredGenus(taxon,
1290 zooHashMap, taxonName, epithetOfTaxon,
1291 genusOfTaxon, taxonNames, zooParentName, syn);
1292
1293 inferredSynonyms.add(inferredGenus);
1294 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1295 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1296
1297
1298 }
1299
1300 if (doWithMisappliedNames){
1301
1302 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1303 Taxon misappliedName = taxonRelationship.getFromTaxon();
1304 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
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 (!taxonNames.isEmpty()){
1314 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1315 ZoologicalName name;
1316 if (!synNotInCDM.isEmpty()){
1317 inferredSynonymsToBeRemoved.clear();
1318
1319 for (Synonym syn :inferredSynonyms){
1320 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1321 if (!synNotInCDM.contains(name.getNameCache())){
1322 inferredSynonymsToBeRemoved.add(syn);
1323 }
1324 }
1325
1326 // Remove identified Synonyms from inferredSynonyms
1327 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1328 inferredSynonyms.remove(synonym);
1329 }
1330 }
1331 }
1332
1333 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1334
1335 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1336 ZoologicalName inferredSynName;
1337 //for all synonyms of the parent...
1338 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1339 TaxonNameBase synName;
1340 Synonym synParent = synonymRelationOfParent.getSynonym();
1341 synName = synParent.getName();
1342
1343 HibernateProxyHelper.deproxy(synParent);
1344
1345 // Set the sourceReference
1346 sourceReference = synParent.getSec();
1347
1348 // Determine the idInSource
1349 String idInSourceParent = getIdInSource(synParent);
1350
1351 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1352 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1353 String synParentInfragenericName = null;
1354 String synParentSpecificEpithet = null;
1355
1356 if (parentSynZooName.isInfraGeneric()){
1357 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1358 }
1359 if (parentSynZooName.isSpecies()){
1360 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1361 }
1362
1363 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1364 synonymsGenus.put(synGenusName, idInSource);
1365 }*/
1366
1367 //for all synonyms of the taxon
1368
1369 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1370
1371 Synonym syn = synonymRelationOfTaxon.getSynonym();
1372 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1373 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1374 synParentGenus,
1375 synParentInfragenericName,
1376 synParentSpecificEpithet, syn, zooHashMap);
1377
1378 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1379 inferredSynonyms.add(potentialCombination);
1380 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1381 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1382
1383 }
1384
1385
1386 }
1387
1388 if (doWithMisappliedNames){
1389
1390 for (TaxonRelationship parentRelationship: taxonRelListParent){
1391
1392 TaxonNameBase misappliedParentName;
1393
1394 Taxon misappliedParent = parentRelationship.getFromTaxon();
1395 misappliedParentName = misappliedParent.getName();
1396
1397 HibernateProxyHelper.deproxy(misappliedParent);
1398
1399 // Set the sourceReference
1400 sourceReference = misappliedParent.getSec();
1401
1402 // Determine the idInSource
1403 String idInSourceParent = getIdInSource(misappliedParent);
1404
1405 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1406 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1407 String synParentInfragenericName = null;
1408 String synParentSpecificEpithet = null;
1409
1410 if (parentSynZooName.isInfraGeneric()){
1411 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1412 }
1413 if (parentSynZooName.isSpecies()){
1414 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1415 }
1416
1417
1418 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1419 Taxon misappliedName = taxonRelationship.getFromTaxon();
1420 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1421 potentialCombination = createPotentialCombination(
1422 idInSourceParent, parentSynZooName, zooMisappliedName,
1423 synParentGenus,
1424 synParentInfragenericName,
1425 synParentSpecificEpithet, misappliedName, zooHashMap);
1426
1427
1428 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1429 inferredSynonyms.add(potentialCombination);
1430 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1431 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1432 }
1433 }
1434 }
1435
1436 if (!taxonNames.isEmpty()){
1437 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1438 ZoologicalName name;
1439 if (!synNotInCDM.isEmpty()){
1440 inferredSynonymsToBeRemoved.clear();
1441 for (Synonym syn :inferredSynonyms){
1442 try{
1443 name = (ZoologicalName) syn.getName();
1444 }catch (ClassCastException e){
1445 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1446 }
1447 if (!synNotInCDM.contains(name.getNameCache())){
1448 inferredSynonymsToBeRemoved.add(syn);
1449 }
1450 }
1451 // Remove identified Synonyms from inferredSynonyms
1452 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1453 inferredSynonyms.remove(synonym);
1454 }
1455 }
1456 }
1457 }
1458 }else {
1459 logger.info("The synonymrelationship type is not defined.");
1460 return inferredSynonyms;
1461 }
1462 }
1463 }
1464
1465 }
1466
1467 return inferredSynonyms;
1468 }
1469
1470 private Synonym createPotentialCombination(String idInSourceParent,
1471 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
1472 String synParentInfragenericName, String synParentSpecificEpithet,
1473 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1474 Synonym potentialCombination;
1475 Reference sourceReference;
1476 ZoologicalName inferredSynName;
1477 HibernateProxyHelper.deproxy(syn);
1478
1479 // Set sourceReference
1480 sourceReference = syn.getSec();
1481
1482
1483 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1484
1485 String synTaxonInfraSpecificName= null;
1486
1487 if (parentSynZooName.isSpecies()){
1488 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1489 }
1490
1491 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1492 synonymsEpithet.add(epithetName);
1493 }*/
1494
1495 //create potential combinations...
1496 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1497
1498 inferredSynName.setGenusOrUninomial(synParentGenus);
1499 if (zooSynName.isSpecies()){
1500 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1501 if (parentSynZooName.isInfraGeneric()){
1502 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1503 }
1504 }
1505 if (zooSynName.isInfraSpecific()){
1506 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1507 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1508 }
1509 if (parentSynZooName.isInfraGeneric()){
1510 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1511 }
1512
1513
1514 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1515
1516 // Set the sourceReference
1517 potentialCombination.setSec(sourceReference);
1518
1519
1520 // Determine the idInSource
1521 String idInSourceSyn= getIdInSource(syn);
1522
1523 if (idInSourceParent != null && idInSourceSyn != null) {
1524 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1525 inferredSynName.addSource(originalSource);
1526 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1527 potentialCombination.addSource(originalSource);
1528 }
1529
1530 inferredSynName.generateTitle();
1531
1532 return potentialCombination;
1533 }
1534
1535 private Synonym createInferredGenus(Taxon taxon,
1536 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1537 String epithetOfTaxon, String genusOfTaxon,
1538 List<String> taxonNames, ZoologicalName zooParentName,
1539 TaxonBase syn) {
1540
1541 Synonym inferredGenus;
1542 TaxonNameBase synName;
1543 ZoologicalName inferredSynName;
1544 synName =syn.getName();
1545 HibernateProxyHelper.deproxy(syn);
1546
1547 // Determine the idInSource
1548 String idInSourceSyn = getIdInSource(syn);
1549 String idInSourceTaxon = getIdInSource(taxon);
1550 // Determine the sourceReference
1551 Reference sourceReference = syn.getSec();
1552
1553 synName = syn.getName();
1554 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1555 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1556 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1557 synonymsEpithet.add(synSpeciesEpithetName);
1558 }*/
1559
1560 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1561 //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...
1562
1563
1564 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1565 if (zooParentName.isInfraGeneric()){
1566 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1567 }
1568
1569 if (taxonName.isSpecies()){
1570 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1571 }
1572 if (taxonName.isInfraSpecific()){
1573 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1574 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1575 }
1576
1577
1578 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1579
1580 // Set the sourceReference
1581 inferredGenus.setSec(sourceReference);
1582
1583 // Add the original source
1584 if (idInSourceSyn != null && idInSourceTaxon != null) {
1585 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1586 inferredGenus.addSource(originalSource);
1587
1588 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1589 inferredSynName.addSource(originalSource);
1590
1591 }
1592
1593 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
1594
1595 inferredSynName.generateTitle();
1596
1597
1598 return inferredGenus;
1599 }
1600
1601 private Synonym createInferredEpithets(Taxon taxon,
1602 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1603 String epithetOfTaxon, String infragenericEpithetOfTaxon,
1604 String infraspecificEpithetOfTaxon, List<String> taxonNames,
1605 TaxonNameBase parentName, TaxonBase syn) {
1606
1607 Synonym inferredEpithet;
1608 TaxonNameBase synName;
1609 ZoologicalName inferredSynName;
1610 HibernateProxyHelper.deproxy(syn);
1611
1612 // Determine the idInSource
1613 String idInSourceSyn = getIdInSource(syn);
1614 String idInSourceTaxon = getIdInSource(taxon);
1615 // Determine the sourceReference
1616 Reference sourceReference = syn.getSec();
1617
1618 synName = syn.getName();
1619 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
1620 String synGenusName = zooSynName.getGenusOrUninomial();
1621 String synInfraGenericEpithet = null;
1622 String synSpecificEpithet = null;
1623
1624 if (zooSynName.getInfraGenericEpithet() != null){
1625 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
1626 }
1627
1628 if (zooSynName.isInfraSpecific()){
1629 synSpecificEpithet = zooSynName.getSpecificEpithet();
1630 }
1631
1632 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1633 synonymsGenus.put(synGenusName, idInSource);
1634 }*/
1635
1636 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1637
1638 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1639 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
1640 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
1641 }
1642 inferredSynName.setGenusOrUninomial(synGenusName);
1643
1644 if (parentName.isInfraGeneric()){
1645 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
1646 }
1647 if (taxonName.isSpecies()){
1648 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1649 }else if (taxonName.isInfraSpecific()){
1650 inferredSynName.setSpecificEpithet(synSpecificEpithet);
1651 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
1652 }
1653
1654 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1655
1656 // Set the sourceReference
1657 inferredEpithet.setSec(sourceReference);
1658
1659 /* Add the original source
1660 if (idInSource != null) {
1661 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1662
1663 // Add the citation
1664 Reference citation = getCitation(syn);
1665 if (citation != null) {
1666 originalSource.setCitation(citation);
1667 inferredEpithet.addSource(originalSource);
1668 }
1669 }*/
1670 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
1671
1672 IdentifiableSource originalSource;
1673 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1674
1675 inferredEpithet.addSource(originalSource);
1676
1677 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1678
1679 inferredSynName.addSource(originalSource);
1680
1681
1682
1683 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
1684
1685 inferredSynName.generateTitle();
1686 return inferredEpithet;
1687 }
1688
1689 /**
1690 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1691 * Very likely only useful for createInferredSynonyms().
1692 * @param uuid
1693 * @param zooHashMap
1694 * @return
1695 */
1696 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
1697 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
1698 if (taxonName == null) {
1699 taxonName = zooHashMap.get(uuid);
1700 }
1701 return taxonName;
1702 }
1703
1704 /**
1705 * Returns the idInSource for a given Synonym.
1706 * @param syn
1707 */
1708 private String getIdInSource(TaxonBase taxonBase) {
1709 String idInSource = null;
1710 Set<IdentifiableSource> sources = taxonBase.getSources();
1711 if (sources.size() == 1) {
1712 IdentifiableSource source = sources.iterator().next();
1713 if (source != null) {
1714 idInSource = source.getIdInSource();
1715 }
1716 } else if (sources.size() > 1) {
1717 int count = 1;
1718 idInSource = "";
1719 for (IdentifiableSource source : sources) {
1720 idInSource += source.getIdInSource();
1721 if (count < sources.size()) {
1722 idInSource += "; ";
1723 }
1724 count++;
1725 }
1726 }
1727
1728 return idInSource;
1729 }
1730
1731
1732 /**
1733 * Returns the citation for a given Synonym.
1734 * @param syn
1735 */
1736 private Reference getCitation(Synonym syn) {
1737 Reference citation = null;
1738 Set<IdentifiableSource> sources = syn.getSources();
1739 if (sources.size() == 1) {
1740 IdentifiableSource source = sources.iterator().next();
1741 if (source != null) {
1742 citation = source.getCitation();
1743 }
1744 } else if (sources.size() > 1) {
1745 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
1746 }
1747
1748 return citation;
1749 }
1750
1751 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
1752 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1753
1754 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
1755 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
1756 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
1757
1758 return inferredSynonyms;
1759 }
1760
1761
1762
1763
1764 }