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