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