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