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