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