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