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