introducing the QueryFactory for to modularize lucene query building
[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.BooleanClause.Occur;
25 import org.apache.lucene.search.BooleanQuery;
26 import org.apache.lucene.search.Query;
27 import org.apache.lucene.search.SortField;
28 import org.apache.lucene.search.TopDocs;
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.stereotype.Service;
31 import org.springframework.transaction.annotation.Propagation;
32 import org.springframework.transaction.annotation.Transactional;
33
34 import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
35 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
36 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
37 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
38 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
39 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
40 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
41 import eu.etaxonomy.cdm.api.service.pager.Pager;
42 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
43 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
44 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
45 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
46 import eu.etaxonomy.cdm.api.service.search.SearchResult;
47 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
48 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
49 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
50 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
51 import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;
52 import eu.etaxonomy.cdm.model.common.CdmBase;
53 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
54 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
55 import eu.etaxonomy.cdm.model.common.Language;
56 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
57 import eu.etaxonomy.cdm.model.common.RelationshipBase;
58 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
59 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
60 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
61 import eu.etaxonomy.cdm.model.description.Feature;
62 import eu.etaxonomy.cdm.model.description.IIdentificationKey;
63 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
64 import eu.etaxonomy.cdm.model.description.TaxonDescription;
65 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
66 import eu.etaxonomy.cdm.model.media.Media;
67 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
68 import eu.etaxonomy.cdm.model.media.MediaUtils;
69 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
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#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
644 */
645 public List<Media> listTaxonDescriptionMedia(Taxon taxon, boolean limitToGalleries, List<String> propertyPath){
646
647 Pager<TaxonDescription> p =
648 descriptionService.getTaxonDescriptions(taxon, null, null, null, null, propertyPath);
649
650 // pars the media and quality parameters
651
652
653 // collect all media of the given taxon
654 List<Media> taxonMedia = new ArrayList<Media>();
655 List<Media> taxonGalleryMedia = new ArrayList<Media>();
656 for(TaxonDescription desc : p.getRecords()){
657
658 if(desc.isImageGallery()){
659 for(DescriptionElementBase element : desc.getElements()){
660 for(Media media : element.getMedia()){
661 taxonGalleryMedia.add(media);
662 }
663 }
664 } else if(!limitToGalleries){
665 for(DescriptionElementBase element : desc.getElements()){
666 for(Media media : element.getMedia()){
667 taxonMedia.add(media);
668 }
669 }
670 }
671
672 }
673
674 taxonGalleryMedia.addAll(taxonMedia);
675 return taxonGalleryMedia;
676 }
677
678 /* (non-Javadoc)
679 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
680 */
681 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
682 return this.dao.findById(listOfIDs);
683 }
684
685 /* (non-Javadoc)
686 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
687 */
688 public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
689 return this.dao.findByUuid(uuid, null ,propertyPaths);
690 }
691
692 /* (non-Javadoc)
693 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
694 */
695 public int countAllRelationships() {
696 return this.dao.countAllRelationships();
697 }
698
699
700
701
702 /* (non-Javadoc)
703 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
704 */
705 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
706 return this.dao.findIdenticalTaxonNames(propertyPath);
707 }
708
709
710 /* (non-Javadoc)
711 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
712 */
713 @Override
714 public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config) throws ReferencedObjectUndeletableException {
715 if (config == null){
716 config = new TaxonDeletionConfigurator();
717 }
718
719 // TaxonNode
720 if (! config.isDeleteTaxonNodes()){
721 if (taxon.getTaxonNodes().size() > 0){
722 String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
723 throw new ReferencedObjectUndeletableException(message);
724 }
725 }
726
727
728 // SynonymRelationShip
729 if (config.isDeleteSynonymRelations()){
730 boolean removeSynonymNameFromHomotypicalGroup = false;
731 for (SynonymRelationship synRel : taxon.getSynonymRelations()){
732 Synonym synonym = synRel.getSynonym();
733 taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);
734 if (config.isDeleteSynonymsIfPossible()){
735 //TODO which value
736 boolean newHomotypicGroupIfNeeded = true;
737 deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);
738 }else{
739 deleteSynonymRelationships(synonym, taxon);
740 }
741 }
742 }
743
744 // TaxonRelationship
745 if (! config.isDeleteTaxonRelationships()){
746 if (taxon.getTaxonRelations().size() > 0){
747 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.";
748 throw new ReferencedObjectUndeletableException(message);
749 }
750 }
751
752
753 // TaxonDescription
754 Set<TaxonDescription> descriptions = taxon.getDescriptions();
755
756 for (TaxonDescription desc: descriptions){
757 if (config.isDeleteDescriptions()){
758 //TODO use description delete configurator ?
759 //FIXME check if description is ALWAYS deletable
760 descriptionService.delete(desc);
761 }else{
762 if (desc.getDescribedSpecimenOrObservations().size()>0){
763 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
764 " which also describes specimens or abservations";
765 throw new ReferencedObjectUndeletableException(message);
766 }
767 }
768 }
769
770
771 //check references with only reverse mapping
772 Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);
773 for (CdmBase referencingObject : referencingObjects){
774 //IIdentificationKeys (Media, Polytomous, MultiAccess)
775 if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){
776 String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
777 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnitBase.class).getTitleCache());
778 throw new ReferencedObjectUndeletableException(message);
779 }
780
781
782 //PolytomousKeyNode
783 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
784 String message = "Taxon can't be deleted as it is used in polytomous key node";
785 throw new ReferencedObjectUndeletableException(message);
786 }
787
788 //TaxonInteraction
789 if (referencingObject.isInstanceOf(TaxonInteraction.class)){
790 String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
791 throw new ReferencedObjectUndeletableException(message);
792 }
793 }
794
795
796 //TaxonNameBase
797 if (config.isDeleteNameIfPossible()){
798 try {
799 nameService.delete(taxon.getName(), config.getNameDeletionConfig());
800 } catch (ReferencedObjectUndeletableException e) {
801 //do nothing
802 if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}
803 }
804 }
805
806 }
807
808 /* (non-Javadoc)
809 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
810 */
811 @Transactional(readOnly = false)
812 @Override
813 public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
814 if (synonym == null){
815 return;
816 }
817 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
818
819 //remove synonymRelationship
820 Set<Taxon> taxonSet = new HashSet<Taxon>();
821 if (taxon != null){
822 taxonSet.add(taxon);
823 }else{
824 taxonSet.addAll(synonym.getAcceptedTaxa());
825 }
826 for (Taxon relatedTaxon : taxonSet){
827 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
828 relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
829 }
830 this.saveOrUpdate(synonym);
831
832 //TODO remove name from homotypical group?
833
834 //remove synonym (if necessary)
835 if (synonym.getSynonymRelations().isEmpty()){
836 TaxonNameBase<?,?> name = synonym.getName();
837 synonym.setName(null);
838 dao.delete(synonym);
839
840 //remove name if possible (and required)
841 if (name != null && removeNameIfPossible){
842 try{
843 nameService.delete(name, new NameDeletionConfigurator());
844 }catch (DataChangeNoRollbackException ex){
845 if (logger.isDebugEnabled())logger.debug("Name wasn't deleted as it is referenced");
846 }
847 }
848 }
849 }
850
851
852 /* (non-Javadoc)
853 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
854 */
855 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
856
857 return this.dao.findIdenticalNamesNew(propertyPath);
858 }
859
860 /* (non-Javadoc)
861 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
862 */
863 public String getPhylumName(TaxonNameBase name){
864 return this.dao.getPhylumName(name);
865 }
866
867 /* (non-Javadoc)
868 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
869 */
870 public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {
871 return dao.deleteSynonymRelationships(syn, taxon);
872 }
873
874 /* (non-Javadoc)
875 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
876 */
877 public long deleteSynonymRelationships(Synonym syn) {
878 return dao.deleteSynonymRelationships(syn, null);
879 }
880
881
882 /* (non-Javadoc)
883 * @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)
884 */
885 public List<SynonymRelationship> listSynonymRelationships(
886 TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
887 List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
888 Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
889
890 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
891 if(numberOfResults > 0) { // no point checking again
892 results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
893 }
894 return results;
895 }
896
897 /* (non-Javadoc)
898 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
899 */
900 @Override
901 public Taxon findBestMatchingTaxon(String taxonName) {
902 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
903 config.setTaxonNameTitle(taxonName);
904 return findBestMatchingTaxon(config);
905 }
906
907
908
909 @Override
910 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
911
912 Taxon bestCandidate = null;
913 try{
914 // 1. search for acceptet taxa
915 List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
916 boolean bestCandidateMatchesSecUuid = false;
917 boolean bestCandidateIsInClassification = false;
918 int countEqualCandidates = 0;
919 for(TaxonBase taxonBaseCandidate : taxonList){
920 if(taxonBaseCandidate instanceof Taxon){
921 Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
922 boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
923 if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
924 continue;
925 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
926 bestCandidate = newCanditate;
927 countEqualCandidates = 1;
928 bestCandidateMatchesSecUuid = true;
929 continue;
930 }
931
932 boolean newCandidateInClassification = isInClassification(newCanditate, config);
933 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
934 continue;
935 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
936 bestCandidate = newCanditate;
937 countEqualCandidates = 1;
938 bestCandidateIsInClassification = true;
939 continue;
940 }
941 if (bestCandidate == null){
942 bestCandidate = newCanditate;
943 countEqualCandidates = 1;
944 continue;
945 }
946
947 }else{ //not Taxon.class
948 continue;
949 }
950 countEqualCandidates++;
951
952 }
953 if (bestCandidate != null){
954 if(countEqualCandidates > 1){
955 logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
956 return bestCandidate;
957 } else {
958 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
959 return bestCandidate;
960 }
961 }
962
963
964 // 2. search for synonyms
965 if (config.isIncludeSynonyms()){
966 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
967 for(TaxonBase taxonBase : synonymList){
968 if(taxonBase instanceof Synonym){
969 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
970 Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
971 if(!acceptetdCandidates.isEmpty()){
972 bestCandidate = acceptetdCandidates.iterator().next();
973 if(acceptetdCandidates.size() == 1){
974 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
975 return bestCandidate;
976 } else {
977 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
978 return bestCandidate;
979 }
980 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
981 }
982 }
983 }
984 }
985
986 } catch (Exception e){
987 logger.error(e);
988 }
989
990 return bestCandidate;
991 }
992
993 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
994 UUID configClassificationUuid = config.getClassificationUuid();
995 if (configClassificationUuid == null){
996 return false;
997 }
998 for (TaxonNode node : taxon.getTaxonNodes()){
999 UUID classUuid = node.getClassification().getUuid();
1000 if (configClassificationUuid.equals(classUuid)){
1001 return true;
1002 }
1003 }
1004 return false;
1005 }
1006
1007 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
1008 UUID configSecUuid = config.getSecUuid();
1009 if (configSecUuid == null){
1010 return false;
1011 }
1012 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
1013 return configSecUuid.equals(taxonSecUuid);
1014 }
1015
1016 /* (non-Javadoc)
1017 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1018 */
1019 @Override
1020 public Synonym findBestMatchingSynonym(String taxonName) {
1021 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
1022 if(! synonymList.isEmpty()){
1023 Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
1024 if(synonymList.size() == 1){
1025 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
1026 return result;
1027 } else {
1028 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
1029 return result;
1030 }
1031 }
1032 return null;
1033 }
1034
1035
1036 /* (non-Javadoc)
1037 * @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)
1038 */
1039 @Override
1040 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
1041 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
1042
1043 Synonym synonym = oldSynonymRelation.getSynonym();
1044 Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
1045 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1046 TaxonNameBase<?,?> synonymName = synonym.getName();
1047 TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
1048 //set default relationship type
1049 if (newSynonymRelationshipType == null){
1050 newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
1051 }
1052 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
1053
1054 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
1055 int hgSize = homotypicGroup.getTypifiedNames().size();
1056 boolean isSingleInGroup = !(hgSize > 1);
1057
1058 if (! isSingleInGroup){
1059 boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
1060 boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
1061 if (isHomotypicToAccepted){
1062 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.";
1063 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
1064 message = String.format(message, homotypicRelatives);
1065 throw new HomotypicalGroupChangeException(message);
1066 }
1067 if (! moveHomotypicGroup){
1068 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.";
1069 throw new HomotypicalGroupChangeException(message);
1070 }
1071 }else{
1072 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
1073 }
1074 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1075
1076 SynonymRelationship result = null;
1077 //move all synonyms to new taxon
1078 List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
1079 for (Synonym syn: homotypicSynonyms){
1080 Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
1081 for (SynonymRelationship synRelation : synRelations){
1082 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
1083 Reference<?> newReference = reference;
1084 if (newReference == null && keepReference){
1085 newReference = synRelation.getCitation();
1086 }
1087 String newRefDetail = referenceDetail;
1088 if (newRefDetail == null && keepReference){
1089 newRefDetail = synRelation.getCitationMicroReference();
1090 }
1091 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
1092 fromTaxon.removeSynonymRelation(synRelation, false);
1093 //
1094 //change homotypic group of synonym if relType is 'homotypic'
1095 // if (newRelTypeIsHomotypic){
1096 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1097 // }
1098 //set result
1099 if (synRelation.equals(oldSynonymRelation)){
1100 result = newSynRelation;
1101 }
1102 }
1103 }
1104
1105 }
1106 saveOrUpdate(newTaxon);
1107 //Assert that there is a result
1108 if (result == null){
1109 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
1110 throw new IllegalStateException(message);
1111 }
1112 return result;
1113 }
1114
1115 /* (non-Javadoc)
1116 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1117 */
1118 @Override
1119 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1120 return dao.getUuidAndTitleCacheTaxon();
1121 }
1122
1123 /* (non-Javadoc)
1124 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1125 */
1126 @Override
1127 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
1128 return dao.getUuidAndTitleCacheSynonym();
1129 }
1130
1131 @Override
1132 public Pager<SearchResult<TaxonBase>> findByFullText(
1133 Class<? extends TaxonBase> clazz, String queryString,
1134 Classification classification, List<Language> languages,
1135 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1136
1137 // // -- set defaults
1138 // Class<? extends TaxonBase> directorySelectClass = TaxonBase.class;
1139 // if(clazz != null){
1140 // directorySelectClass = clazz;
1141 // }
1142 return null;
1143
1144 }
1145
1146 @Override
1147 public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
1148 Class<? extends DescriptionElementBase> clazz, String queryString,
1149 Classification classification, List<Feature> features, List<Language> languages,
1150 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1151
1152 // -- set defaults
1153 Class<? extends DescriptionElementBase> directorySelectClass = DescriptionElementBase.class;
1154 if(clazz != null){
1155 directorySelectClass = clazz;
1156 }
1157
1158 // queryString = queryString.toLowerCase();
1159
1160 StringBuilder luceneQueryTemplate = new StringBuilder();
1161 BooleanQuery finalQuery = new BooleanQuery();
1162 BooleanQuery textQuery = new BooleanQuery();
1163 Set<String> freetextFields = new HashSet<String>();
1164
1165 LuceneSearch luceneSearch = new LuceneSearch(getSession(), directorySelectClass);
1166 QueryFactory queryBuilder = new QueryFactory(luceneSearch);
1167
1168 // ---- search criteria
1169
1170 // luceneQueryTemplate.append("+(");
1171 // luceneQueryTemplate.append("titleCache:(%1$s) ");
1172 freetextFields.add("titleCache");
1173 textQuery.add(queryBuilder.newTermQuery("titleCache", queryString), Occur.SHOULD);
1174
1175 // common name
1176 freetextFields.add("name");
1177
1178 Query nameQuery;
1179 if(languages == null || languages.size() == 0){
1180 // luceneQueryTemplate.append("name:(%1$s) ");
1181 nameQuery = queryBuilder.newTermQuery("name", queryString);
1182 } else {
1183 nameQuery = new BooleanQuery();
1184 BooleanQuery languageSubQuery = new BooleanQuery();
1185 // luceneQueryTemplate.append("(+name:(%1$s) ");
1186 for(Language lang : languages){
1187 // luceneQueryTemplate.append(" +language.uuid:" + lang.getUuid().toString());
1188 languageSubQuery.add(queryBuilder.newTermQuery("language.uuid", lang.getUuid().toString()), Occur.SHOULD);
1189 }
1190 ((BooleanQuery) nameQuery).add(queryBuilder.newTermQuery("name", queryString), Occur.MUST);
1191 ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);
1192 // luceneQueryTemplate.append(")");
1193 }
1194 textQuery.add(nameQuery, Occur.SHOULD);
1195
1196
1197 // text field from TextData
1198 freetextFields.add("text.ALL");
1199 // appendLocalizedFieldQuery("text", languages, luceneQueryTemplate).append(" ");
1200 textQuery.add(queryBuilder.newLocalizedTermQuery("text", queryString, languages), Occur.SHOULD);
1201
1202 // --- TermBase fields - by representation ----
1203 // state field from CategoricalData
1204 freetextFields.add("states.state.representation.ALL");
1205 // appendLocalizedFieldQuery("states.state.representation", languages, luceneQueryTemplate).append(" ");
1206 textQuery.add(queryBuilder.newLocalizedTermQuery("states.state.representation", queryString, languages), Occur.SHOULD);
1207
1208 // state field from CategoricalData
1209 freetextFields.add("states.modifyingText.ALL");
1210 // appendLocalizedFieldQuery("states.modifyingText", languages, luceneQueryTemplate).append(" ");
1211 textQuery.add(queryBuilder.newLocalizedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD);
1212 // luceneQueryTemplate.append(") ");
1213
1214 finalQuery.add(textQuery, Occur.MUST);
1215 // --- classification ----
1216
1217 if(classification != null){
1218 // luceneQueryTemplate.append("+inDescription.taxon.taxonNodes.classification.id:").append(PaddedIntegerBridge.paddInteger(classification.getId())).append(" ");
1219 finalQuery.add(queryBuilder.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
1220 }
1221
1222 // --- IdentifieableEntity fields - by uuid
1223 if(features != null && features.size() > 0 ){
1224 finalQuery.add(queryBuilder.newEntityUuidQuery("feature.uuid", features), Occur.MUST);
1225 // luceneQueryTemplate.append("+feature.uuid:(");
1226 // for(Feature feature : features){
1227 // luceneQueryTemplate.append(feature.getUuid()).append(" ");
1228 // }
1229 // luceneQueryTemplate.append(") ");
1230 }
1231
1232 // the description must be associated with a taxon
1233 // luceneQueryTemplate.append("+inDescription.taxon.id:[ " + PaddedIntegerBridge.paddInteger(0) + " TO " + PaddedIntegerBridge.paddInteger(Integer.MAX_VALUE) + "] ");
1234 //luceneQueryTemplate.append("-inDescription.taxon.id:" + PaddedIntegerBridge.NULL_STRING);
1235 finalQuery.add(queryBuilder.newIdNotNullQuery("inDescription.taxon.id"), Occur.MUST);
1236
1237 // String luceneQueryStr = String.format(luceneQueryTemplate.toString(), queryString);
1238
1239 // --- sort fields
1240 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", false)};
1241
1242 // ---- execute criteria
1243
1244
1245 // Query luceneQuery = luceneSearch.parse(luceneQueryStr);
1246 TopDocs topDocsResultSet = luceneSearch.executeSearch(finalQuery, clazz, pageSize, pageNumber, sortFields);
1247
1248 String[] highlightFields = null;
1249 if(highlightFragments){
1250 highlightFields = freetextFields.toArray(new String[freetextFields.size()]);
1251 }
1252
1253 // initialize taxa, thighlight matches ....
1254 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, finalQuery);
1255 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1256 topDocsResultSet, highlightFields, dao, "inDescription.taxon.id", propertyPaths);
1257
1258 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, searchResults.size(), pageSize, searchResults);
1259
1260 }
1261
1262 /**
1263 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1264 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1265 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1266 *
1267 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1268 * or {@link MultilanguageTextFieldBridge }
1269 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1270 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1271 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1272 *
1273 * TODO move to utiliy class !!!!!!!!
1274 */
1275 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1276
1277 if(stringBuilder == null){
1278 stringBuilder = new StringBuilder();
1279 }
1280 if(languages == null || languages.size() == 0){
1281 stringBuilder.append(name + ".ALL:(%1$s) ");
1282 } else {
1283 for(Language lang : languages){
1284 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
1285 }
1286 }
1287 return stringBuilder;
1288 }
1289
1290 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1291 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1292 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1293
1294 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1295
1296
1297 UUID uuid= taxon.getName().getUuid();
1298 ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);
1299 String epithetOfTaxon = null;
1300 String infragenericEpithetOfTaxon = null;
1301 String infraspecificEpithetOfTaxon = null;
1302 if (taxonName.isSpecies()){
1303 epithetOfTaxon= taxonName.getSpecificEpithet();
1304 } else if (taxonName.isInfraGeneric()){
1305 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1306 } else if (taxonName.isInfraSpecific()){
1307 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1308 }
1309 String genusOfTaxon = taxonName.getGenusOrUninomial();
1310 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1311 List<String> taxonNames = new ArrayList<String>();
1312
1313 for (TaxonNode node: nodes){
1314 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1315 // List<String> synonymsEpithet = new ArrayList<String>();
1316
1317 if (node.getClassification().equals(classification)){
1318 if (!node.isTopmostNode()){
1319 TaxonNode parent = (TaxonNode)node.getParent();
1320 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1321 TaxonNameBase parentName = parent.getTaxon().getName();
1322 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1323 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1324 Rank rankOfTaxon = taxonName.getRank();
1325
1326
1327 //create inferred synonyms for species, subspecies
1328 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1329
1330 Synonym inferredEpithet = null;
1331 Synonym inferredGenus = null;
1332 Synonym potentialCombination = null;
1333
1334 List<String> propertyPaths = new ArrayList<String>();
1335 propertyPaths.add("synonym");
1336 propertyPaths.add("synonym.name");
1337 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1338 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1339
1340 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1341 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1342
1343 List<TaxonRelationship> taxonRelListParent = null;
1344 List<TaxonRelationship> taxonRelListTaxon = null;
1345 if (doWithMisappliedNames){
1346 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1347 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1348 }
1349
1350
1351 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1352 Set<String> genusNames = new HashSet<String>();
1353
1354 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1355 Synonym syn = synonymRelationOfParent.getSynonym();
1356
1357 inferredEpithet = createInferredEpithets(taxon,
1358 zooHashMap, taxonName, epithetOfTaxon,
1359 infragenericEpithetOfTaxon,
1360 infraspecificEpithetOfTaxon,
1361 taxonNames, parentName,
1362 syn);
1363
1364
1365 inferredSynonyms.add(inferredEpithet);
1366 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1367 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1368 }
1369
1370 if (doWithMisappliedNames){
1371
1372 for (TaxonRelationship taxonRelationship: taxonRelListParent){
1373 Taxon misappliedName = taxonRelationship.getFromTaxon();
1374
1375 inferredEpithet = createInferredEpithets(taxon,
1376 zooHashMap, taxonName, epithetOfTaxon,
1377 infragenericEpithetOfTaxon,
1378 infraspecificEpithetOfTaxon,
1379 taxonNames, parentName,
1380 misappliedName);
1381
1382 inferredSynonyms.add(inferredEpithet);
1383 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1384 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1385 }
1386 }
1387
1388 if (!taxonNames.isEmpty()){
1389 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1390 ZoologicalName name;
1391 if (!synNotInCDM.isEmpty()){
1392 inferredSynonymsToBeRemoved.clear();
1393
1394 for (Synonym syn :inferredSynonyms){
1395 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1396 if (!synNotInCDM.contains(name.getNameCache())){
1397 inferredSynonymsToBeRemoved.add(syn);
1398 }
1399 }
1400
1401 // Remove identified Synonyms from inferredSynonyms
1402 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1403 inferredSynonyms.remove(synonym);
1404 }
1405 }
1406 }
1407
1408 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1409
1410
1411 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1412 TaxonNameBase synName;
1413 ZoologicalName inferredSynName;
1414
1415 Synonym syn = synonymRelationOfTaxon.getSynonym();
1416 inferredGenus = createInferredGenus(taxon,
1417 zooHashMap, taxonName, epithetOfTaxon,
1418 genusOfTaxon, taxonNames, zooParentName, syn);
1419
1420 inferredSynonyms.add(inferredGenus);
1421 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1422 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1423
1424
1425 }
1426
1427 if (doWithMisappliedNames){
1428
1429 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1430 Taxon misappliedName = taxonRelationship.getFromTaxon();
1431 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
1432
1433 inferredSynonyms.add(inferredGenus);
1434 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1435 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1436 }
1437 }
1438
1439
1440 if (!taxonNames.isEmpty()){
1441 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1442 ZoologicalName name;
1443 if (!synNotInCDM.isEmpty()){
1444 inferredSynonymsToBeRemoved.clear();
1445
1446 for (Synonym syn :inferredSynonyms){
1447 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1448 if (!synNotInCDM.contains(name.getNameCache())){
1449 inferredSynonymsToBeRemoved.add(syn);
1450 }
1451 }
1452
1453 // Remove identified Synonyms from inferredSynonyms
1454 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1455 inferredSynonyms.remove(synonym);
1456 }
1457 }
1458 }
1459
1460 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1461
1462 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1463 ZoologicalName inferredSynName;
1464 //for all synonyms of the parent...
1465 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1466 TaxonNameBase synName;
1467 Synonym synParent = synonymRelationOfParent.getSynonym();
1468 synName = synParent.getName();
1469
1470 HibernateProxyHelper.deproxy(synParent);
1471
1472 // Set the sourceReference
1473 sourceReference = synParent.getSec();
1474
1475 // Determine the idInSource
1476 String idInSourceParent = getIdInSource(synParent);
1477
1478 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1479 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1480 String synParentInfragenericName = null;
1481 String synParentSpecificEpithet = null;
1482
1483 if (parentSynZooName.isInfraGeneric()){
1484 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1485 }
1486 if (parentSynZooName.isSpecies()){
1487 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1488 }
1489
1490 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1491 synonymsGenus.put(synGenusName, idInSource);
1492 }*/
1493
1494 //for all synonyms of the taxon
1495
1496 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1497
1498 Synonym syn = synonymRelationOfTaxon.getSynonym();
1499 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1500 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1501 synParentGenus,
1502 synParentInfragenericName,
1503 synParentSpecificEpithet, syn, zooHashMap);
1504
1505 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1506 inferredSynonyms.add(potentialCombination);
1507 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1508 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1509
1510 }
1511
1512
1513 }
1514
1515 if (doWithMisappliedNames){
1516
1517 for (TaxonRelationship parentRelationship: taxonRelListParent){
1518
1519 TaxonNameBase misappliedParentName;
1520
1521 Taxon misappliedParent = parentRelationship.getFromTaxon();
1522 misappliedParentName = misappliedParent.getName();
1523
1524 HibernateProxyHelper.deproxy(misappliedParent);
1525
1526 // Set the sourceReference
1527 sourceReference = misappliedParent.getSec();
1528
1529 // Determine the idInSource
1530 String idInSourceParent = getIdInSource(misappliedParent);
1531
1532 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1533 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1534 String synParentInfragenericName = null;
1535 String synParentSpecificEpithet = null;
1536
1537 if (parentSynZooName.isInfraGeneric()){
1538 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1539 }
1540 if (parentSynZooName.isSpecies()){
1541 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1542 }
1543
1544
1545 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1546 Taxon misappliedName = taxonRelationship.getFromTaxon();
1547 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1548 potentialCombination = createPotentialCombination(
1549 idInSourceParent, parentSynZooName, zooMisappliedName,
1550 synParentGenus,
1551 synParentInfragenericName,
1552 synParentSpecificEpithet, misappliedName, zooHashMap);
1553
1554
1555 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1556 inferredSynonyms.add(potentialCombination);
1557 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1558 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1559 }
1560 }
1561 }
1562
1563 if (!taxonNames.isEmpty()){
1564 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1565 ZoologicalName name;
1566 if (!synNotInCDM.isEmpty()){
1567 inferredSynonymsToBeRemoved.clear();
1568 for (Synonym syn :inferredSynonyms){
1569 try{
1570 name = (ZoologicalName) syn.getName();
1571 }catch (ClassCastException e){
1572 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1573 }
1574 if (!synNotInCDM.contains(name.getNameCache())){
1575 inferredSynonymsToBeRemoved.add(syn);
1576 }
1577 }
1578 // Remove identified Synonyms from inferredSynonyms
1579 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1580 inferredSynonyms.remove(synonym);
1581 }
1582 }
1583 }
1584 }
1585 }else {
1586 logger.info("The synonymrelationship type is not defined.");
1587 return inferredSynonyms;
1588 }
1589 }
1590 }
1591
1592 }
1593
1594 return inferredSynonyms;
1595 }
1596
1597 private Synonym createPotentialCombination(String idInSourceParent,
1598 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
1599 String synParentInfragenericName, String synParentSpecificEpithet,
1600 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1601 Synonym potentialCombination;
1602 Reference sourceReference;
1603 ZoologicalName inferredSynName;
1604 HibernateProxyHelper.deproxy(syn);
1605
1606 // Set sourceReference
1607 sourceReference = syn.getSec();
1608 if (sourceReference == null){
1609 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1610 //TODO:Remove
1611 if (!parentSynZooName.getTaxa().isEmpty()){
1612 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
1613
1614 sourceReference = taxon.getSec();
1615 }
1616 }
1617 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1618
1619 String synTaxonInfraSpecificName= null;
1620
1621 if (parentSynZooName.isSpecies()){
1622 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1623 }
1624
1625 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1626 synonymsEpithet.add(epithetName);
1627 }*/
1628
1629 //create potential combinations...
1630 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1631
1632 inferredSynName.setGenusOrUninomial(synParentGenus);
1633 if (zooSynName.isSpecies()){
1634 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1635 if (parentSynZooName.isInfraGeneric()){
1636 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1637 }
1638 }
1639 if (zooSynName.isInfraSpecific()){
1640 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1641 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1642 }
1643 if (parentSynZooName.isInfraGeneric()){
1644 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1645 }
1646
1647
1648 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1649
1650 // Set the sourceReference
1651 potentialCombination.setSec(sourceReference);
1652
1653
1654 // Determine the idInSource
1655 String idInSourceSyn= getIdInSource(syn);
1656
1657 if (idInSourceParent != null && idInSourceSyn != null) {
1658 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1659 inferredSynName.addSource(originalSource);
1660 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1661 potentialCombination.addSource(originalSource);
1662 }
1663
1664 inferredSynName.generateTitle();
1665
1666 return potentialCombination;
1667 }
1668
1669 private Synonym createInferredGenus(Taxon taxon,
1670 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1671 String epithetOfTaxon, String genusOfTaxon,
1672 List<String> taxonNames, ZoologicalName zooParentName,
1673 TaxonBase syn) {
1674
1675 Synonym inferredGenus;
1676 TaxonNameBase synName;
1677 ZoologicalName inferredSynName;
1678 synName =syn.getName();
1679 HibernateProxyHelper.deproxy(syn);
1680
1681 // Determine the idInSource
1682 String idInSourceSyn = getIdInSource(syn);
1683 String idInSourceTaxon = getIdInSource(taxon);
1684 // Determine the sourceReference
1685 Reference sourceReference = syn.getSec();
1686
1687 //logger.warn(sourceReference.getTitleCache());
1688
1689 synName = syn.getName();
1690 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1691 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1692 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1693 synonymsEpithet.add(synSpeciesEpithetName);
1694 }*/
1695
1696 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1697 //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...
1698
1699
1700 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1701 if (zooParentName.isInfraGeneric()){
1702 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1703 }
1704
1705 if (taxonName.isSpecies()){
1706 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1707 }
1708 if (taxonName.isInfraSpecific()){
1709 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1710 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1711 }
1712
1713
1714 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1715
1716 // Set the sourceReference
1717 inferredGenus.setSec(sourceReference);
1718
1719 // Add the original source
1720 if (idInSourceSyn != null && idInSourceTaxon != null) {
1721 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1722 inferredGenus.addSource(originalSource);
1723
1724 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1725 inferredSynName.addSource(originalSource);
1726 originalSource = null;
1727
1728 }else{
1729 logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
1730 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1731 inferredGenus.addSource(originalSource);
1732
1733 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1734 inferredSynName.addSource(originalSource);
1735 originalSource = null;
1736 }
1737
1738 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
1739
1740 inferredSynName.generateTitle();
1741
1742
1743 return inferredGenus;
1744 }
1745
1746 private Synonym createInferredEpithets(Taxon taxon,
1747 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1748 String epithetOfTaxon, String infragenericEpithetOfTaxon,
1749 String infraspecificEpithetOfTaxon, List<String> taxonNames,
1750 TaxonNameBase parentName, TaxonBase syn) {
1751
1752 Synonym inferredEpithet;
1753 TaxonNameBase synName;
1754 ZoologicalName inferredSynName;
1755 HibernateProxyHelper.deproxy(syn);
1756
1757 // Determine the idInSource
1758 String idInSourceSyn = getIdInSource(syn);
1759 String idInSourceTaxon = getIdInSource(taxon);
1760 // Determine the sourceReference
1761 Reference sourceReference = syn.getSec();
1762
1763 if (sourceReference == null){
1764 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1765 //TODO:Remove
1766 System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
1767 sourceReference = taxon.getSec();
1768 }
1769
1770 synName = syn.getName();
1771 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
1772 String synGenusName = zooSynName.getGenusOrUninomial();
1773 String synInfraGenericEpithet = null;
1774 String synSpecificEpithet = null;
1775
1776 if (zooSynName.getInfraGenericEpithet() != null){
1777 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
1778 }
1779
1780 if (zooSynName.isInfraSpecific()){
1781 synSpecificEpithet = zooSynName.getSpecificEpithet();
1782 }
1783
1784 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1785 synonymsGenus.put(synGenusName, idInSource);
1786 }*/
1787
1788 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1789
1790 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1791 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
1792 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
1793 }
1794 inferredSynName.setGenusOrUninomial(synGenusName);
1795
1796 if (parentName.isInfraGeneric()){
1797 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
1798 }
1799 if (taxonName.isSpecies()){
1800 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1801 }else if (taxonName.isInfraSpecific()){
1802 inferredSynName.setSpecificEpithet(synSpecificEpithet);
1803 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
1804 }
1805
1806 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1807
1808 // Set the sourceReference
1809 inferredEpithet.setSec(sourceReference);
1810
1811 /* Add the original source
1812 if (idInSource != null) {
1813 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1814
1815 // Add the citation
1816 Reference citation = getCitation(syn);
1817 if (citation != null) {
1818 originalSource.setCitation(citation);
1819 inferredEpithet.addSource(originalSource);
1820 }
1821 }*/
1822 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
1823
1824 IdentifiableSource originalSource;
1825 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1826
1827 inferredEpithet.addSource(originalSource);
1828
1829 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1830
1831 inferredSynName.addSource(originalSource);
1832
1833
1834
1835 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
1836
1837 inferredSynName.generateTitle();
1838 return inferredEpithet;
1839 }
1840
1841 /**
1842 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1843 * Very likely only useful for createInferredSynonyms().
1844 * @param uuid
1845 * @param zooHashMap
1846 * @return
1847 */
1848 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
1849 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
1850 if (taxonName == null) {
1851 taxonName = zooHashMap.get(uuid);
1852 }
1853 return taxonName;
1854 }
1855
1856 /**
1857 * Returns the idInSource for a given Synonym.
1858 * @param syn
1859 */
1860 private String getIdInSource(TaxonBase taxonBase) {
1861 String idInSource = null;
1862 Set<IdentifiableSource> sources = taxonBase.getSources();
1863 if (sources.size() == 1) {
1864 IdentifiableSource source = sources.iterator().next();
1865 if (source != null) {
1866 idInSource = source.getIdInSource();
1867 }
1868 } else if (sources.size() > 1) {
1869 int count = 1;
1870 idInSource = "";
1871 for (IdentifiableSource source : sources) {
1872 idInSource += source.getIdInSource();
1873 if (count < sources.size()) {
1874 idInSource += "; ";
1875 }
1876 count++;
1877 }
1878 } else if (sources.size() == 0){
1879 logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
1880 }
1881
1882
1883 return idInSource;
1884 }
1885
1886
1887 /**
1888 * Returns the citation for a given Synonym.
1889 * @param syn
1890 */
1891 private Reference getCitation(Synonym syn) {
1892 Reference citation = null;
1893 Set<IdentifiableSource> sources = syn.getSources();
1894 if (sources.size() == 1) {
1895 IdentifiableSource source = sources.iterator().next();
1896 if (source != null) {
1897 citation = source.getCitation();
1898 }
1899 } else if (sources.size() > 1) {
1900 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
1901 }
1902
1903 return citation;
1904 }
1905
1906 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
1907 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1908
1909 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
1910 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
1911 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
1912
1913 return inferredSynonyms;
1914 }
1915
1916
1917
1918
1919 }