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