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