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