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