refactoring a bit for clearer code
[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<Feature> features, List<Language> languages,
1224 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints
1225 , List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1226
1227 LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, features, languages, highlightFragments);
1228 LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);
1229
1230 LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearchByDescriptionElement, luceneSearchByTaxonBase);
1231
1232 // --- execute search
1233 TopDocs topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
1234
1235 // --- initialize taxa, thighlight matches ....
1236 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
1237 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1238 topDocsResultSet, multiSearch.getHighlightFields(), dao, "inDescription.taxon.id", propertyPaths);
1239
1240 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, topDocsResultSet.totalHits, pageSize, searchResults);
1241
1242 }
1243
1244
1245 /**
1246 * @param clazz
1247 * @param queryString
1248 * @param classification
1249 * @param features
1250 * @param languages
1251 * @param highlightFragments
1252 * @param directorySelectClass
1253 * @return
1254 */
1255 protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Feature> features,
1256 List<Language> languages, boolean highlightFragments) {
1257 BooleanQuery finalQuery = new BooleanQuery();
1258 BooleanQuery textQuery = new BooleanQuery();
1259
1260 LuceneSearch luceneSearch = new LuceneSearch(getSession(), DescriptionElementBase.class);
1261 QueryFactory queryFactory = new QueryFactory(luceneSearch);
1262
1263 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", false)};
1264 luceneSearch.setSortFields(sortFields);
1265
1266 // ---- search criteria
1267 luceneSearch.setClazz(clazz);
1268 textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
1269
1270 // common name
1271 Query nameQuery;
1272 if(languages == null || languages.size() == 0){
1273 nameQuery = queryFactory.newTermQuery("name", queryString);
1274 } else {
1275 nameQuery = new BooleanQuery();
1276 BooleanQuery languageSubQuery = new BooleanQuery();
1277 for(Language lang : languages){
1278 languageSubQuery.add(queryFactory.newTermQuery("language.uuid", lang.getUuid().toString()), Occur.SHOULD);
1279 }
1280 ((BooleanQuery) nameQuery).add(queryFactory.newTermQuery("name", queryString), Occur.MUST);
1281 ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);
1282 }
1283 textQuery.add(nameQuery, Occur.SHOULD);
1284
1285
1286 // text field from TextData
1287 textQuery.add(queryFactory.newLocalizedTermQuery("text", queryString, languages), Occur.SHOULD);
1288
1289 // --- TermBase fields - by representation ----
1290 // state field from CategoricalData
1291 textQuery.add(queryFactory.newLocalizedTermQuery("states.state.representation", queryString, languages), Occur.SHOULD);
1292
1293 // state field from CategoricalData
1294 textQuery.add(queryFactory.newLocalizedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD);
1295
1296 finalQuery.add(textQuery, Occur.MUST);
1297 // --- classification ----
1298
1299 if(classification != null){
1300 finalQuery.add(queryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
1301 }
1302
1303 // --- IdentifieableEntity fields - by uuid
1304 if(features != null && features.size() > 0 ){
1305 finalQuery.add(queryFactory.newEntityUuidQuery("feature.uuid", features), Occur.MUST);
1306 }
1307
1308 // the description must be associated with a taxon
1309 finalQuery.add(queryFactory.newIdNotNullQuery("inDescription.taxon.id"), Occur.MUST);
1310
1311 luceneSearch.setQuery(finalQuery);
1312
1313 if(highlightFragments){
1314 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
1315 }
1316 return luceneSearch;
1317 }
1318
1319 /**
1320 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1321 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1322 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1323 *
1324 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1325 * or {@link MultilanguageTextFieldBridge }
1326 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1327 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1328 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1329 *
1330 * TODO move to utiliy class !!!!!!!!
1331 */
1332 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1333
1334 if(stringBuilder == null){
1335 stringBuilder = new StringBuilder();
1336 }
1337 if(languages == null || languages.size() == 0){
1338 stringBuilder.append(name + ".ALL:(%1$s) ");
1339 } else {
1340 for(Language lang : languages){
1341 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
1342 }
1343 }
1344 return stringBuilder;
1345 }
1346
1347 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1348 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1349 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1350
1351 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1352
1353
1354 UUID uuid= taxon.getName().getUuid();
1355 ZoologicalName taxonName = getZoologicalName(uuid, zooHashMap);
1356 String epithetOfTaxon = null;
1357 String infragenericEpithetOfTaxon = null;
1358 String infraspecificEpithetOfTaxon = null;
1359 if (taxonName.isSpecies()){
1360 epithetOfTaxon= taxonName.getSpecificEpithet();
1361 } else if (taxonName.isInfraGeneric()){
1362 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1363 } else if (taxonName.isInfraSpecific()){
1364 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1365 }
1366 String genusOfTaxon = taxonName.getGenusOrUninomial();
1367 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1368 List<String> taxonNames = new ArrayList<String>();
1369
1370 for (TaxonNode node: nodes){
1371 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1372 // List<String> synonymsEpithet = new ArrayList<String>();
1373
1374 if (node.getClassification().equals(classification)){
1375 if (!node.isTopmostNode()){
1376 TaxonNode parent = (TaxonNode)node.getParent();
1377 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1378 TaxonNameBase parentName = parent.getTaxon().getName();
1379 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1380 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1381 Rank rankOfTaxon = taxonName.getRank();
1382
1383
1384 //create inferred synonyms for species, subspecies
1385 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1386
1387 Synonym inferredEpithet = null;
1388 Synonym inferredGenus = null;
1389 Synonym potentialCombination = null;
1390
1391 List<String> propertyPaths = new ArrayList<String>();
1392 propertyPaths.add("synonym");
1393 propertyPaths.add("synonym.name");
1394 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1395 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1396
1397 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1398 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1399
1400 List<TaxonRelationship> taxonRelListParent = null;
1401 List<TaxonRelationship> taxonRelListTaxon = null;
1402 if (doWithMisappliedNames){
1403 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1404 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1405 }
1406
1407
1408 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1409 Set<String> genusNames = new HashSet<String>();
1410
1411 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1412 Synonym syn = synonymRelationOfParent.getSynonym();
1413
1414 inferredEpithet = createInferredEpithets(taxon,
1415 zooHashMap, taxonName, epithetOfTaxon,
1416 infragenericEpithetOfTaxon,
1417 infraspecificEpithetOfTaxon,
1418 taxonNames, parentName,
1419 syn);
1420
1421
1422 inferredSynonyms.add(inferredEpithet);
1423 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1424 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1425 }
1426
1427 if (doWithMisappliedNames){
1428
1429 for (TaxonRelationship taxonRelationship: taxonRelListParent){
1430 Taxon misappliedName = taxonRelationship.getFromTaxon();
1431
1432 inferredEpithet = createInferredEpithets(taxon,
1433 zooHashMap, taxonName, epithetOfTaxon,
1434 infragenericEpithetOfTaxon,
1435 infraspecificEpithetOfTaxon,
1436 taxonNames, parentName,
1437 misappliedName);
1438
1439 inferredSynonyms.add(inferredEpithet);
1440 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1441 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1442 }
1443 }
1444
1445 if (!taxonNames.isEmpty()){
1446 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1447 ZoologicalName name;
1448 if (!synNotInCDM.isEmpty()){
1449 inferredSynonymsToBeRemoved.clear();
1450
1451 for (Synonym syn :inferredSynonyms){
1452 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1453 if (!synNotInCDM.contains(name.getNameCache())){
1454 inferredSynonymsToBeRemoved.add(syn);
1455 }
1456 }
1457
1458 // Remove identified Synonyms from inferredSynonyms
1459 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1460 inferredSynonyms.remove(synonym);
1461 }
1462 }
1463 }
1464
1465 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1466
1467
1468 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1469 TaxonNameBase synName;
1470 ZoologicalName inferredSynName;
1471
1472 Synonym syn = synonymRelationOfTaxon.getSynonym();
1473 inferredGenus = createInferredGenus(taxon,
1474 zooHashMap, taxonName, epithetOfTaxon,
1475 genusOfTaxon, taxonNames, zooParentName, syn);
1476
1477 inferredSynonyms.add(inferredGenus);
1478 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1479 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1480
1481
1482 }
1483
1484 if (doWithMisappliedNames){
1485
1486 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1487 Taxon misappliedName = taxonRelationship.getFromTaxon();
1488 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
1489
1490 inferredSynonyms.add(inferredGenus);
1491 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1492 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1493 }
1494 }
1495
1496
1497 if (!taxonNames.isEmpty()){
1498 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1499 ZoologicalName name;
1500 if (!synNotInCDM.isEmpty()){
1501 inferredSynonymsToBeRemoved.clear();
1502
1503 for (Synonym syn :inferredSynonyms){
1504 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1505 if (!synNotInCDM.contains(name.getNameCache())){
1506 inferredSynonymsToBeRemoved.add(syn);
1507 }
1508 }
1509
1510 // Remove identified Synonyms from inferredSynonyms
1511 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1512 inferredSynonyms.remove(synonym);
1513 }
1514 }
1515 }
1516
1517 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1518
1519 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1520 ZoologicalName inferredSynName;
1521 //for all synonyms of the parent...
1522 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1523 TaxonNameBase synName;
1524 Synonym synParent = synonymRelationOfParent.getSynonym();
1525 synName = synParent.getName();
1526
1527 HibernateProxyHelper.deproxy(synParent);
1528
1529 // Set the sourceReference
1530 sourceReference = synParent.getSec();
1531
1532 // Determine the idInSource
1533 String idInSourceParent = getIdInSource(synParent);
1534
1535 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1536 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1537 String synParentInfragenericName = null;
1538 String synParentSpecificEpithet = null;
1539
1540 if (parentSynZooName.isInfraGeneric()){
1541 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1542 }
1543 if (parentSynZooName.isSpecies()){
1544 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1545 }
1546
1547 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1548 synonymsGenus.put(synGenusName, idInSource);
1549 }*/
1550
1551 //for all synonyms of the taxon
1552
1553 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1554
1555 Synonym syn = synonymRelationOfTaxon.getSynonym();
1556 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1557 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1558 synParentGenus,
1559 synParentInfragenericName,
1560 synParentSpecificEpithet, syn, zooHashMap);
1561
1562 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1563 inferredSynonyms.add(potentialCombination);
1564 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1565 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1566
1567 }
1568
1569
1570 }
1571
1572 if (doWithMisappliedNames){
1573
1574 for (TaxonRelationship parentRelationship: taxonRelListParent){
1575
1576 TaxonNameBase misappliedParentName;
1577
1578 Taxon misappliedParent = parentRelationship.getFromTaxon();
1579 misappliedParentName = misappliedParent.getName();
1580
1581 HibernateProxyHelper.deproxy(misappliedParent);
1582
1583 // Set the sourceReference
1584 sourceReference = misappliedParent.getSec();
1585
1586 // Determine the idInSource
1587 String idInSourceParent = getIdInSource(misappliedParent);
1588
1589 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1590 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1591 String synParentInfragenericName = null;
1592 String synParentSpecificEpithet = null;
1593
1594 if (parentSynZooName.isInfraGeneric()){
1595 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1596 }
1597 if (parentSynZooName.isSpecies()){
1598 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1599 }
1600
1601
1602 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1603 Taxon misappliedName = taxonRelationship.getFromTaxon();
1604 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1605 potentialCombination = createPotentialCombination(
1606 idInSourceParent, parentSynZooName, zooMisappliedName,
1607 synParentGenus,
1608 synParentInfragenericName,
1609 synParentSpecificEpithet, misappliedName, zooHashMap);
1610
1611
1612 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1613 inferredSynonyms.add(potentialCombination);
1614 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1615 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1616 }
1617 }
1618 }
1619
1620 if (!taxonNames.isEmpty()){
1621 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1622 ZoologicalName name;
1623 if (!synNotInCDM.isEmpty()){
1624 inferredSynonymsToBeRemoved.clear();
1625 for (Synonym syn :inferredSynonyms){
1626 try{
1627 name = (ZoologicalName) syn.getName();
1628 }catch (ClassCastException e){
1629 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1630 }
1631 if (!synNotInCDM.contains(name.getNameCache())){
1632 inferredSynonymsToBeRemoved.add(syn);
1633 }
1634 }
1635 // Remove identified Synonyms from inferredSynonyms
1636 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1637 inferredSynonyms.remove(synonym);
1638 }
1639 }
1640 }
1641 }
1642 }else {
1643 logger.info("The synonymrelationship type is not defined.");
1644 return inferredSynonyms;
1645 }
1646 }
1647 }
1648
1649 }
1650
1651 return inferredSynonyms;
1652 }
1653
1654 private Synonym createPotentialCombination(String idInSourceParent,
1655 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
1656 String synParentInfragenericName, String synParentSpecificEpithet,
1657 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1658 Synonym potentialCombination;
1659 Reference sourceReference;
1660 ZoologicalName inferredSynName;
1661 HibernateProxyHelper.deproxy(syn);
1662
1663 // Set sourceReference
1664 sourceReference = syn.getSec();
1665 if (sourceReference == null){
1666 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1667 //TODO:Remove
1668 if (!parentSynZooName.getTaxa().isEmpty()){
1669 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
1670
1671 sourceReference = taxon.getSec();
1672 }
1673 }
1674 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1675
1676 String synTaxonInfraSpecificName= null;
1677
1678 if (parentSynZooName.isSpecies()){
1679 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1680 }
1681
1682 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1683 synonymsEpithet.add(epithetName);
1684 }*/
1685
1686 //create potential combinations...
1687 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1688
1689 inferredSynName.setGenusOrUninomial(synParentGenus);
1690 if (zooSynName.isSpecies()){
1691 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1692 if (parentSynZooName.isInfraGeneric()){
1693 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1694 }
1695 }
1696 if (zooSynName.isInfraSpecific()){
1697 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1698 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1699 }
1700 if (parentSynZooName.isInfraGeneric()){
1701 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1702 }
1703
1704
1705 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1706
1707 // Set the sourceReference
1708 potentialCombination.setSec(sourceReference);
1709
1710
1711 // Determine the idInSource
1712 String idInSourceSyn= getIdInSource(syn);
1713
1714 if (idInSourceParent != null && idInSourceSyn != null) {
1715 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1716 inferredSynName.addSource(originalSource);
1717 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1718 potentialCombination.addSource(originalSource);
1719 }
1720
1721 inferredSynName.generateTitle();
1722
1723 return potentialCombination;
1724 }
1725
1726 private Synonym createInferredGenus(Taxon taxon,
1727 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1728 String epithetOfTaxon, String genusOfTaxon,
1729 List<String> taxonNames, ZoologicalName zooParentName,
1730 TaxonBase syn) {
1731
1732 Synonym inferredGenus;
1733 TaxonNameBase synName;
1734 ZoologicalName inferredSynName;
1735 synName =syn.getName();
1736 HibernateProxyHelper.deproxy(syn);
1737
1738 // Determine the idInSource
1739 String idInSourceSyn = getIdInSource(syn);
1740 String idInSourceTaxon = getIdInSource(taxon);
1741 // Determine the sourceReference
1742 Reference sourceReference = syn.getSec();
1743
1744 //logger.warn(sourceReference.getTitleCache());
1745
1746 synName = syn.getName();
1747 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1748 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1749 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1750 synonymsEpithet.add(synSpeciesEpithetName);
1751 }*/
1752
1753 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1754 //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...
1755
1756
1757 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1758 if (zooParentName.isInfraGeneric()){
1759 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1760 }
1761
1762 if (taxonName.isSpecies()){
1763 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1764 }
1765 if (taxonName.isInfraSpecific()){
1766 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1767 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1768 }
1769
1770
1771 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1772
1773 // Set the sourceReference
1774 inferredGenus.setSec(sourceReference);
1775
1776 // Add the original source
1777 if (idInSourceSyn != null && idInSourceTaxon != null) {
1778 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1779 inferredGenus.addSource(originalSource);
1780
1781 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1782 inferredSynName.addSource(originalSource);
1783 originalSource = null;
1784
1785 }else{
1786 logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
1787 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1788 inferredGenus.addSource(originalSource);
1789
1790 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
1791 inferredSynName.addSource(originalSource);
1792 originalSource = null;
1793 }
1794
1795 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
1796
1797 inferredSynName.generateTitle();
1798
1799
1800 return inferredGenus;
1801 }
1802
1803 private Synonym createInferredEpithets(Taxon taxon,
1804 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1805 String epithetOfTaxon, String infragenericEpithetOfTaxon,
1806 String infraspecificEpithetOfTaxon, List<String> taxonNames,
1807 TaxonNameBase parentName, TaxonBase syn) {
1808
1809 Synonym inferredEpithet;
1810 TaxonNameBase synName;
1811 ZoologicalName inferredSynName;
1812 HibernateProxyHelper.deproxy(syn);
1813
1814 // Determine the idInSource
1815 String idInSourceSyn = getIdInSource(syn);
1816 String idInSourceTaxon = getIdInSource(taxon);
1817 // Determine the sourceReference
1818 Reference sourceReference = syn.getSec();
1819
1820 if (sourceReference == null){
1821 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1822 //TODO:Remove
1823 System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
1824 sourceReference = taxon.getSec();
1825 }
1826
1827 synName = syn.getName();
1828 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
1829 String synGenusName = zooSynName.getGenusOrUninomial();
1830 String synInfraGenericEpithet = null;
1831 String synSpecificEpithet = null;
1832
1833 if (zooSynName.getInfraGenericEpithet() != null){
1834 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
1835 }
1836
1837 if (zooSynName.isInfraSpecific()){
1838 synSpecificEpithet = zooSynName.getSpecificEpithet();
1839 }
1840
1841 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1842 synonymsGenus.put(synGenusName, idInSource);
1843 }*/
1844
1845 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1846
1847 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
1848 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
1849 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
1850 }
1851 inferredSynName.setGenusOrUninomial(synGenusName);
1852
1853 if (parentName.isInfraGeneric()){
1854 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
1855 }
1856 if (taxonName.isSpecies()){
1857 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1858 }else if (taxonName.isInfraSpecific()){
1859 inferredSynName.setSpecificEpithet(synSpecificEpithet);
1860 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
1861 }
1862
1863 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1864
1865 // Set the sourceReference
1866 inferredEpithet.setSec(sourceReference);
1867
1868 /* Add the original source
1869 if (idInSource != null) {
1870 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
1871
1872 // Add the citation
1873 Reference citation = getCitation(syn);
1874 if (citation != null) {
1875 originalSource.setCitation(citation);
1876 inferredEpithet.addSource(originalSource);
1877 }
1878 }*/
1879 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
1880
1881 IdentifiableSource originalSource;
1882 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1883
1884 inferredEpithet.addSource(originalSource);
1885
1886 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
1887
1888 inferredSynName.addSource(originalSource);
1889
1890
1891
1892 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
1893
1894 inferredSynName.generateTitle();
1895 return inferredEpithet;
1896 }
1897
1898 /**
1899 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
1900 * Very likely only useful for createInferredSynonyms().
1901 * @param uuid
1902 * @param zooHashMap
1903 * @return
1904 */
1905 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
1906 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
1907 if (taxonName == null) {
1908 taxonName = zooHashMap.get(uuid);
1909 }
1910 return taxonName;
1911 }
1912
1913 /**
1914 * Returns the idInSource for a given Synonym.
1915 * @param syn
1916 */
1917 private String getIdInSource(TaxonBase taxonBase) {
1918 String idInSource = null;
1919 Set<IdentifiableSource> sources = taxonBase.getSources();
1920 if (sources.size() == 1) {
1921 IdentifiableSource source = sources.iterator().next();
1922 if (source != null) {
1923 idInSource = source.getIdInSource();
1924 }
1925 } else if (sources.size() > 1) {
1926 int count = 1;
1927 idInSource = "";
1928 for (IdentifiableSource source : sources) {
1929 idInSource += source.getIdInSource();
1930 if (count < sources.size()) {
1931 idInSource += "; ";
1932 }
1933 count++;
1934 }
1935 } else if (sources.size() == 0){
1936 logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
1937 }
1938
1939
1940 return idInSource;
1941 }
1942
1943
1944 /**
1945 * Returns the citation for a given Synonym.
1946 * @param syn
1947 */
1948 private Reference getCitation(Synonym syn) {
1949 Reference citation = null;
1950 Set<IdentifiableSource> sources = syn.getSources();
1951 if (sources.size() == 1) {
1952 IdentifiableSource source = sources.iterator().next();
1953 if (source != null) {
1954 citation = source.getCitation();
1955 }
1956 } else if (sources.size() > 1) {
1957 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
1958 }
1959
1960 return citation;
1961 }
1962
1963 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
1964 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1965
1966 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
1967 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
1968 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
1969
1970 return inferredSynonyms;
1971 }
1972
1973
1974
1975
1976 }