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