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