integrating trunk to hibernate4 branch. Probably no real changes.
[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.Propagation;
32 import org.springframework.transaction.annotation.Transactional;
33
34 import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
35 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
36 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
37 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
38 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
39 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
40 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
41 import eu.etaxonomy.cdm.api.service.pager.Pager;
42 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
43 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
44 import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;
45 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
46 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
47 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
48 import eu.etaxonomy.cdm.api.service.search.SearchResult;
49 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
50 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
51 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
52 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
53 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
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(propagation = Propagation.SUPPORTS, 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 List<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,
504 Integer limit, Integer start, List<String> propertyPaths) {
505
506 List<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new ArrayList<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 List<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, List<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 List<Taxon> taxa = new ArrayList<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(), 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()), 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 luceneSearch.setQuery(finalQuery);
1533
1534 if(highlightFragments){
1535 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
1536 }
1537 return luceneSearch;
1538 }
1539
1540 /**
1541 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1542 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1543 * This method is a convenient means to retrieve a Lucene query string for such the fields.
1544 *
1545 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1546 * or {@link MultilanguageTextFieldBridge }
1547 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1548 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1549 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1550 *
1551 * TODO move to utiliy class !!!!!!!!
1552 */
1553 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1554
1555 if(stringBuilder == null){
1556 stringBuilder = new StringBuilder();
1557 }
1558 if(languages == null || languages.size() == 0){
1559 stringBuilder.append(name + ".ALL:(%1$s) ");
1560 } else {
1561 for(Language lang : languages){
1562 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
1563 }
1564 }
1565 return stringBuilder;
1566 }
1567
1568 @Override
1569 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1570 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1571 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1572
1573 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1574
1575
1576 UUID nameUuid= taxon.getName().getUuid();
1577 ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);
1578 String epithetOfTaxon = null;
1579 String infragenericEpithetOfTaxon = null;
1580 String infraspecificEpithetOfTaxon = null;
1581 if (taxonName.isSpecies()){
1582 epithetOfTaxon= taxonName.getSpecificEpithet();
1583 } else if (taxonName.isInfraGeneric()){
1584 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1585 } else if (taxonName.isInfraSpecific()){
1586 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1587 }
1588 String genusOfTaxon = taxonName.getGenusOrUninomial();
1589 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1590 List<String> taxonNames = new ArrayList<String>();
1591
1592 for (TaxonNode node: nodes){
1593 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1594 // List<String> synonymsEpithet = new ArrayList<String>();
1595
1596 if (node.getClassification().equals(classification)){
1597 if (!node.isTopmostNode()){
1598 TaxonNode parent = node.getParent();
1599 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1600 TaxonNameBase<?,?> parentName = parent.getTaxon().getName();
1601 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1602 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1603 Rank rankOfTaxon = taxonName.getRank();
1604
1605
1606 //create inferred synonyms for species, subspecies
1607 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1608
1609 Synonym inferredEpithet = null;
1610 Synonym inferredGenus = null;
1611 Synonym potentialCombination = null;
1612
1613 List<String> propertyPaths = new ArrayList<String>();
1614 propertyPaths.add("synonym");
1615 propertyPaths.add("synonym.name");
1616 List<OrderHint> orderHints = new ArrayList<OrderHint>();
1617 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1618
1619 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1620 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1621
1622 List<TaxonRelationship> taxonRelListParent = null;
1623 List<TaxonRelationship> taxonRelListTaxon = null;
1624 if (doWithMisappliedNames){
1625 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1626 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1627 }
1628
1629
1630 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1631
1632
1633 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1634 Synonym syn = synonymRelationOfParent.getSynonym();
1635
1636 inferredEpithet = createInferredEpithets(taxon,
1637 zooHashMap, taxonName, epithetOfTaxon,
1638 infragenericEpithetOfTaxon,
1639 infraspecificEpithetOfTaxon,
1640 taxonNames, parentName,
1641 syn);
1642
1643
1644 inferredSynonyms.add(inferredEpithet);
1645 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1646 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1647 }
1648
1649 if (doWithMisappliedNames){
1650
1651 for (TaxonRelationship taxonRelationship: taxonRelListParent){
1652 Taxon misappliedName = taxonRelationship.getFromTaxon();
1653
1654 inferredEpithet = createInferredEpithets(taxon,
1655 zooHashMap, taxonName, epithetOfTaxon,
1656 infragenericEpithetOfTaxon,
1657 infraspecificEpithetOfTaxon,
1658 taxonNames, parentName,
1659 misappliedName);
1660
1661 inferredSynonyms.add(inferredEpithet);
1662 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1663 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1664 }
1665 }
1666
1667 if (!taxonNames.isEmpty()){
1668 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1669 ZoologicalName name;
1670 if (!synNotInCDM.isEmpty()){
1671 inferredSynonymsToBeRemoved.clear();
1672
1673 for (Synonym syn :inferredSynonyms){
1674 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1675 if (!synNotInCDM.contains(name.getNameCache())){
1676 inferredSynonymsToBeRemoved.add(syn);
1677 }
1678 }
1679
1680 // Remove identified Synonyms from inferredSynonyms
1681 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1682 inferredSynonyms.remove(synonym);
1683 }
1684 }
1685 }
1686
1687 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1688
1689
1690 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1691 TaxonNameBase synName;
1692 ZoologicalName inferredSynName;
1693
1694 Synonym syn = synonymRelationOfTaxon.getSynonym();
1695 inferredGenus = createInferredGenus(taxon,
1696 zooHashMap, taxonName, epithetOfTaxon,
1697 genusOfTaxon, taxonNames, zooParentName, syn);
1698
1699 inferredSynonyms.add(inferredGenus);
1700 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1701 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1702
1703
1704 }
1705
1706 if (doWithMisappliedNames){
1707
1708 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1709 Taxon misappliedName = taxonRelationship.getFromTaxon();
1710 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
1711
1712 inferredSynonyms.add(inferredGenus);
1713 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1714 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1715 }
1716 }
1717
1718
1719 if (!taxonNames.isEmpty()){
1720 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1721 ZoologicalName name;
1722 if (!synNotInCDM.isEmpty()){
1723 inferredSynonymsToBeRemoved.clear();
1724
1725 for (Synonym syn :inferredSynonyms){
1726 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1727 if (!synNotInCDM.contains(name.getNameCache())){
1728 inferredSynonymsToBeRemoved.add(syn);
1729 }
1730 }
1731
1732 // Remove identified Synonyms from inferredSynonyms
1733 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1734 inferredSynonyms.remove(synonym);
1735 }
1736 }
1737 }
1738
1739 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1740
1741 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1742 ZoologicalName inferredSynName;
1743 //for all synonyms of the parent...
1744 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1745 TaxonNameBase synName;
1746 Synonym synParent = synonymRelationOfParent.getSynonym();
1747 synName = synParent.getName();
1748
1749 HibernateProxyHelper.deproxy(synParent);
1750
1751 // Set the sourceReference
1752 sourceReference = synParent.getSec();
1753
1754 // Determine the idInSource
1755 String idInSourceParent = getIdInSource(synParent);
1756
1757 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1758 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1759 String synParentInfragenericName = null;
1760 String synParentSpecificEpithet = null;
1761
1762 if (parentSynZooName.isInfraGeneric()){
1763 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1764 }
1765 if (parentSynZooName.isSpecies()){
1766 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1767 }
1768
1769 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1770 synonymsGenus.put(synGenusName, idInSource);
1771 }*/
1772
1773 //for all synonyms of the taxon
1774
1775 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1776
1777 Synonym syn = synonymRelationOfTaxon.getSynonym();
1778 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1779 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1780 synParentGenus,
1781 synParentInfragenericName,
1782 synParentSpecificEpithet, syn, zooHashMap);
1783
1784 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1785 inferredSynonyms.add(potentialCombination);
1786 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1787 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1788
1789 }
1790
1791
1792 }
1793
1794 if (doWithMisappliedNames){
1795
1796 for (TaxonRelationship parentRelationship: taxonRelListParent){
1797
1798 TaxonNameBase misappliedParentName;
1799
1800 Taxon misappliedParent = parentRelationship.getFromTaxon();
1801 misappliedParentName = misappliedParent.getName();
1802
1803 HibernateProxyHelper.deproxy(misappliedParent);
1804
1805 // Set the sourceReference
1806 sourceReference = misappliedParent.getSec();
1807
1808 // Determine the idInSource
1809 String idInSourceParent = getIdInSource(misappliedParent);
1810
1811 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1812 String synParentGenus = parentSynZooName.getGenusOrUninomial();
1813 String synParentInfragenericName = null;
1814 String synParentSpecificEpithet = null;
1815
1816 if (parentSynZooName.isInfraGeneric()){
1817 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1818 }
1819 if (parentSynZooName.isSpecies()){
1820 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1821 }
1822
1823
1824 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1825 Taxon misappliedName = taxonRelationship.getFromTaxon();
1826 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1827 potentialCombination = createPotentialCombination(
1828 idInSourceParent, parentSynZooName, zooMisappliedName,
1829 synParentGenus,
1830 synParentInfragenericName,
1831 synParentSpecificEpithet, misappliedName, zooHashMap);
1832
1833
1834 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1835 inferredSynonyms.add(potentialCombination);
1836 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1837 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1838 }
1839 }
1840 }
1841
1842 if (!taxonNames.isEmpty()){
1843 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1844 ZoologicalName name;
1845 if (!synNotInCDM.isEmpty()){
1846 inferredSynonymsToBeRemoved.clear();
1847 for (Synonym syn :inferredSynonyms){
1848 try{
1849 name = (ZoologicalName) syn.getName();
1850 }catch (ClassCastException e){
1851 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1852 }
1853 if (!synNotInCDM.contains(name.getNameCache())){
1854 inferredSynonymsToBeRemoved.add(syn);
1855 }
1856 }
1857 // Remove identified Synonyms from inferredSynonyms
1858 for (Synonym synonym : inferredSynonymsToBeRemoved) {
1859 inferredSynonyms.remove(synonym);
1860 }
1861 }
1862 }
1863 }
1864 }else {
1865 logger.info("The synonymrelationship type is not defined.");
1866 return inferredSynonyms;
1867 }
1868 }
1869 }
1870
1871 }
1872
1873 return inferredSynonyms;
1874 }
1875
1876 private Synonym createPotentialCombination(String idInSourceParent,
1877 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
1878 String synParentInfragenericName, String synParentSpecificEpithet,
1879 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1880 Synonym potentialCombination;
1881 Reference sourceReference;
1882 ZoologicalName inferredSynName;
1883 HibernateProxyHelper.deproxy(syn);
1884
1885 // Set sourceReference
1886 sourceReference = syn.getSec();
1887 if (sourceReference == null){
1888 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1889 //TODO:Remove
1890 if (!parentSynZooName.getTaxa().isEmpty()){
1891 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
1892
1893 sourceReference = taxon.getSec();
1894 }
1895 }
1896 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1897
1898 String synTaxonInfraSpecificName= null;
1899
1900 if (parentSynZooName.isSpecies()){
1901 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1902 }
1903
1904 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1905 synonymsEpithet.add(epithetName);
1906 }*/
1907
1908 //create potential combinations...
1909 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1910
1911 inferredSynName.setGenusOrUninomial(synParentGenus);
1912 if (zooSynName.isSpecies()){
1913 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1914 if (parentSynZooName.isInfraGeneric()){
1915 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1916 }
1917 }
1918 if (zooSynName.isInfraSpecific()){
1919 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1920 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1921 }
1922 if (parentSynZooName.isInfraGeneric()){
1923 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1924 }
1925
1926
1927 potentialCombination = Synonym.NewInstance(inferredSynName, null);
1928
1929 // Set the sourceReference
1930 potentialCombination.setSec(sourceReference);
1931
1932
1933 // Determine the idInSource
1934 String idInSourceSyn= getIdInSource(syn);
1935
1936 if (idInSourceParent != null && idInSourceSyn != null) {
1937 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1938 inferredSynName.addSource(originalSource);
1939 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1940 potentialCombination.addSource(originalSource);
1941 }
1942
1943 inferredSynName.generateTitle();
1944
1945 return potentialCombination;
1946 }
1947
1948 private Synonym createInferredGenus(Taxon taxon,
1949 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1950 String epithetOfTaxon, String genusOfTaxon,
1951 List<String> taxonNames, ZoologicalName zooParentName,
1952 TaxonBase syn) {
1953
1954 Synonym inferredGenus;
1955 TaxonNameBase synName;
1956 ZoologicalName inferredSynName;
1957 synName =syn.getName();
1958 HibernateProxyHelper.deproxy(syn);
1959
1960 // Determine the idInSource
1961 String idInSourceSyn = getIdInSource(syn);
1962 String idInSourceTaxon = getIdInSource(taxon);
1963 // Determine the sourceReference
1964 Reference sourceReference = syn.getSec();
1965
1966 //logger.warn(sourceReference.getTitleCache());
1967
1968 synName = syn.getName();
1969 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1970 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1971 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1972 synonymsEpithet.add(synSpeciesEpithetName);
1973 }*/
1974
1975 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1976 //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...
1977
1978
1979 inferredSynName.setGenusOrUninomial(genusOfTaxon);
1980 if (zooParentName.isInfraGeneric()){
1981 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1982 }
1983
1984 if (taxonName.isSpecies()){
1985 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1986 }
1987 if (taxonName.isInfraSpecific()){
1988 inferredSynName.setSpecificEpithet(epithetOfTaxon);
1989 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1990 }
1991
1992
1993 inferredGenus = Synonym.NewInstance(inferredSynName, null);
1994
1995 // Set the sourceReference
1996 inferredGenus.setSec(sourceReference);
1997
1998 // Add the original source
1999 if (idInSourceSyn != null && idInSourceTaxon != null) {
2000 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2001 inferredGenus.addSource(originalSource);
2002
2003 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2004 inferredSynName.addSource(originalSource);
2005 originalSource = null;
2006
2007 }else{
2008 logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
2009 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2010 inferredGenus.addSource(originalSource);
2011
2012 originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2013 inferredSynName.addSource(originalSource);
2014 originalSource = null;
2015 }
2016
2017 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
2018
2019 inferredSynName.generateTitle();
2020
2021
2022 return inferredGenus;
2023 }
2024
2025 private Synonym createInferredEpithets(Taxon taxon,
2026 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
2027 String epithetOfTaxon, String infragenericEpithetOfTaxon,
2028 String infraspecificEpithetOfTaxon, List<String> taxonNames,
2029 TaxonNameBase parentName, TaxonBase syn) {
2030
2031 Synonym inferredEpithet;
2032 TaxonNameBase<?,?> synName;
2033 ZoologicalName inferredSynName;
2034 HibernateProxyHelper.deproxy(syn);
2035
2036 // Determine the idInSource
2037 String idInSourceSyn = getIdInSource(syn);
2038 String idInSourceTaxon = getIdInSource(taxon);
2039 // Determine the sourceReference
2040 Reference<?> sourceReference = syn.getSec();
2041
2042 if (sourceReference == null){
2043 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2044 //TODO:Remove
2045 System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
2046 sourceReference = taxon.getSec();
2047 }
2048
2049 synName = syn.getName();
2050 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
2051 String synGenusName = zooSynName.getGenusOrUninomial();
2052 String synInfraGenericEpithet = null;
2053 String synSpecificEpithet = null;
2054
2055 if (zooSynName.getInfraGenericEpithet() != null){
2056 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
2057 }
2058
2059 if (zooSynName.isInfraSpecific()){
2060 synSpecificEpithet = zooSynName.getSpecificEpithet();
2061 }
2062
2063 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2064 synonymsGenus.put(synGenusName, idInSource);
2065 }*/
2066
2067 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
2068
2069 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2070 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
2071 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
2072 }
2073 inferredSynName.setGenusOrUninomial(synGenusName);
2074
2075 if (parentName.isInfraGeneric()){
2076 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
2077 }
2078 if (taxonName.isSpecies()){
2079 inferredSynName.setSpecificEpithet(epithetOfTaxon);
2080 }else if (taxonName.isInfraSpecific()){
2081 inferredSynName.setSpecificEpithet(synSpecificEpithet);
2082 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
2083 }
2084
2085 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
2086
2087 // Set the sourceReference
2088 inferredEpithet.setSec(sourceReference);
2089
2090 /* Add the original source
2091 if (idInSource != null) {
2092 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2093
2094 // Add the citation
2095 Reference citation = getCitation(syn);
2096 if (citation != null) {
2097 originalSource.setCitation(citation);
2098 inferredEpithet.addSource(originalSource);
2099 }
2100 }*/
2101 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
2102
2103
2104 IdentifiableSource originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2105
2106 inferredEpithet.addSource(originalSource);
2107
2108 originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2109
2110 inferredSynName.addSource(originalSource);
2111
2112
2113
2114 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
2115
2116 inferredSynName.generateTitle();
2117 return inferredEpithet;
2118 }
2119
2120 /**
2121 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2122 * Very likely only useful for createInferredSynonyms().
2123 * @param uuid
2124 * @param zooHashMap
2125 * @return
2126 */
2127 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
2128 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
2129 if (taxonName == null) {
2130 taxonName = zooHashMap.get(uuid);
2131 }
2132 return taxonName;
2133 }
2134
2135 /**
2136 * Returns the idInSource for a given Synonym.
2137 * @param syn
2138 */
2139 private String getIdInSource(TaxonBase taxonBase) {
2140 String idInSource = null;
2141 Set<IdentifiableSource> sources = taxonBase.getSources();
2142 if (sources.size() == 1) {
2143 IdentifiableSource source = sources.iterator().next();
2144 if (source != null) {
2145 idInSource = source.getIdInSource();
2146 }
2147 } else if (sources.size() > 1) {
2148 int count = 1;
2149 idInSource = "";
2150 for (IdentifiableSource source : sources) {
2151 idInSource += source.getIdInSource();
2152 if (count < sources.size()) {
2153 idInSource += "; ";
2154 }
2155 count++;
2156 }
2157 } else if (sources.size() == 0){
2158 logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
2159 }
2160
2161
2162 return idInSource;
2163 }
2164
2165
2166 /**
2167 * Returns the citation for a given Synonym.
2168 * @param syn
2169 */
2170 private Reference getCitation(Synonym syn) {
2171 Reference citation = null;
2172 Set<IdentifiableSource> sources = syn.getSources();
2173 if (sources.size() == 1) {
2174 IdentifiableSource source = sources.iterator().next();
2175 if (source != null) {
2176 citation = source.getCitation();
2177 }
2178 } else if (sources.size() > 1) {
2179 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
2180 }
2181
2182 return citation;
2183 }
2184
2185 @Override
2186 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
2187 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
2188
2189 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
2190 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
2191 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
2192
2193 return inferredSynonyms;
2194 }
2195
2196 /* (non-Javadoc)
2197 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2198 */
2199 @Override
2200 public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {
2201
2202 // TODO quickly implemented, create according dao !!!!
2203 Set<TaxonNode> nodes = new HashSet<TaxonNode>();
2204 Set<Classification> classifications = new HashSet<Classification>();
2205 List<Classification> list = new ArrayList<Classification>();
2206
2207 if (taxonBase == null) {
2208 return list;
2209 }
2210
2211 taxonBase = load(taxonBase.getUuid());
2212
2213 if (taxonBase instanceof Taxon) {
2214 nodes.addAll(((Taxon)taxonBase).getTaxonNodes());
2215 } else {
2216 for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {
2217 nodes.addAll(taxon.getTaxonNodes());
2218 }
2219 }
2220 for (TaxonNode node : nodes) {
2221 classifications.add(node.getClassification());
2222 }
2223 list.addAll(classifications);
2224 return list;
2225 }
2226
2227
2228
2229
2230
2231
2232 }