further fixes for subareas #4220
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TaxonServiceImpl.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.api.service;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.EnumSet;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.UUID;
23
24 import javax.persistence.EntityNotFoundException;
25
26 import org.apache.log4j.Logger;
27 import org.apache.lucene.index.CorruptIndexException;
28 import org.apache.lucene.queryParser.ParseException;
29 import org.apache.lucene.search.BooleanClause.Occur;
30 import org.apache.lucene.search.BooleanFilter;
31 import org.apache.lucene.search.BooleanQuery;
32 import org.apache.lucene.search.DocIdSet;
33 import org.apache.lucene.search.Query;
34 import org.apache.lucene.search.QueryWrapperFilter;
35 import org.apache.lucene.search.SortField;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.stereotype.Service;
38 import org.springframework.transaction.annotation.Transactional;
39
40 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
41 import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
42 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
43 import eu.etaxonomy.cdm.api.service.config.SynonymDeletionConfigurator;
44 import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
45 import eu.etaxonomy.cdm.api.service.config.TaxonNodeDeletionConfigurator.ChildHandling;
46 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
47 import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
48 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
49 import eu.etaxonomy.cdm.api.service.pager.Pager;
50 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
51 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
52 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
53 import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;
54 import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearchException;
55 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
56 import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
57 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
58 import eu.etaxonomy.cdm.api.service.search.SearchResult;
59 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
60 import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
61 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
62 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
63 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
64 import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
65 import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;
66 import eu.etaxonomy.cdm.model.CdmBaseType;
67 import eu.etaxonomy.cdm.model.common.CdmBase;
68 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
69 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
70 import eu.etaxonomy.cdm.model.common.Language;
71 import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
72 import eu.etaxonomy.cdm.model.common.OriginalSourceType;
73 import eu.etaxonomy.cdm.model.common.RelationshipBase;
74 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
75 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
76 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
77 import eu.etaxonomy.cdm.model.description.DescriptionBase;
78 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
79 import eu.etaxonomy.cdm.model.description.Distribution;
80 import eu.etaxonomy.cdm.model.description.Feature;
81 import eu.etaxonomy.cdm.model.description.IIdentificationKey;
82 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
83 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
84 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
85 import eu.etaxonomy.cdm.model.description.TaxonDescription;
86 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
87 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
88 import eu.etaxonomy.cdm.model.location.NamedArea;
89 import eu.etaxonomy.cdm.model.media.Media;
90 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
91 import eu.etaxonomy.cdm.model.media.MediaUtils;
92 import eu.etaxonomy.cdm.model.molecular.Amplification;
93 import eu.etaxonomy.cdm.model.molecular.DnaSample;
94 import eu.etaxonomy.cdm.model.molecular.Sequence;
95 import eu.etaxonomy.cdm.model.molecular.SingleRead;
96 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
97 import eu.etaxonomy.cdm.model.name.NameRelationship;
98 import eu.etaxonomy.cdm.model.name.Rank;
99 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
100 import eu.etaxonomy.cdm.model.name.ZoologicalName;
101 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
102 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
103 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
104 import eu.etaxonomy.cdm.model.reference.Reference;
105 import eu.etaxonomy.cdm.model.taxon.Classification;
106 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
107 import eu.etaxonomy.cdm.model.taxon.Synonym;
108 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
109 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
110 import eu.etaxonomy.cdm.model.taxon.Taxon;
111 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
112 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
113 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
114 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
115 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
116 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
117 import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
118 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
119 import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
120 import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
121 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
122 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
123 import eu.etaxonomy.cdm.persistence.query.MatchMode;
124 import eu.etaxonomy.cdm.persistence.query.OrderHint;
125 import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
126 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
127
128
129 /**
130 * @author a.kohlbecker
131 * @date 10.09.2010
132 *
133 */
134 @Service
135 @Transactional(readOnly = true)
136 public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
137 private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
138
139 public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";
140
141 public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";
142
143 public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";
144
145
146 @Autowired
147 private ITaxonNameDao nameDao;
148
149 @Autowired
150 private INameService nameService;
151
152 @Autowired
153 private ITaxonNodeService nodeService;
154
155 @Autowired
156 private ICdmGenericDao genericDao;
157
158 @Autowired
159 private IDescriptionService descriptionService;
160
161 @Autowired
162 private IOrderedTermVocabularyDao orderedVocabularyDao;
163
164 @Autowired
165 private IOccurrenceDao occurrenceDao;
166
167 @Autowired
168 private IClassificationDao classificationDao;
169
170 @Autowired
171 private AbstractBeanInitializer beanInitializer;
172
173 @Autowired
174 private ILuceneIndexToolProvider luceneIndexToolProvider;
175
176 /**
177 * Constructor
178 */
179 public TaxonServiceImpl(){
180 if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
181 }
182
183 /**
184 * FIXME Candidate for harmonization
185 * rename searchByName ?
186 */
187 @Override
188 public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
189 return dao.getTaxaByName(name, sec);
190 }
191
192 /**
193 * FIXME Candidate for harmonization
194 * list(Synonym.class, ...)
195 * (non-Javadoc)
196 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
197 */
198 @Override
199 public List<Synonym> getAllSynonyms(int limit, int start) {
200 return dao.getAllSynonyms(limit, start);
201 }
202
203 /**
204 * FIXME Candidate for harmonization
205 * list(Taxon.class, ...)
206 * (non-Javadoc)
207 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
208 */
209 @Override
210 public List<Taxon> getAllTaxa(int limit, int start) {
211 return dao.getAllTaxa(limit, start);
212 }
213
214 /**
215 * FIXME Candidate for harmonization
216 * merge with getRootTaxa(Reference sec, ..., ...)
217 * (non-Javadoc)
218 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
219 */
220 @Override
221 public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
222 if (cdmFetch == null){
223 cdmFetch = CdmFetch.NO_FETCH();
224 }
225 return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
226 }
227
228
229 /* (non-Javadoc)
230 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
231 */
232 @Override
233 public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
234 return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
235 }
236
237 /* (non-Javadoc)
238 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
239 */
240 @Override
241 public List<RelationshipBase> getAllRelationships(int limit, int start){
242 return dao.getAllRelationships(limit, start);
243 }
244
245 /**
246 * FIXME Candidate for harmonization
247 * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
248 */
249 @Override
250 @Deprecated
251 public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
252
253 String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
254 UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
255 OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
256 (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
257 return taxonRelTypeVocabulary;
258 }
259
260
261
262 /*
263 * (non-Javadoc)
264 * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
265 */
266 @Override
267 @Transactional(readOnly = false)
268 public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
269
270 TaxonNameBase<?,?> synonymName = synonym.getName();
271 synonymName.removeTaxonBase(synonym);
272 TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
273 taxonName.removeTaxonBase(acceptedTaxon);
274
275 synonym.setName(taxonName);
276 acceptedTaxon.setName(synonymName);
277
278 // the accepted taxon needs a new uuid because the concept has changed
279 // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
280 //acceptedTaxon.setUuid(UUID.randomUUID());
281 }
282
283
284 /* (non-Javadoc)
285 * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
286 */
287
288 @Override
289 @Transactional(readOnly = false)
290 public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
291
292 TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
293 TaxonNameBase<?,?> synonymName = synonym.getName();
294 HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
295
296 //check synonym is not homotypic
297 if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
298 String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
299 throw new HomotypicalGroupChangeException(message);
300 }
301
302 Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
303
304 SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
305 List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
306 Set<NameRelationship> basionymsAndReplacedSynonyms = synonymHomotypicGroup.getBasionymAndReplacedSynonymRelations();
307
308 for (Synonym heteroSynonym : heteroSynonyms){
309 if (synonym.equals(heteroSynonym)){
310 acceptedTaxon.removeSynonym(heteroSynonym, false);
311
312 }else{
313 //move synonyms in same homotypic group to new accepted taxon
314 heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
315 }
316 }
317
318 //synonym.getName().removeTaxonBase(synonym);
319
320 if (deleteSynonym){
321 // deleteSynonym(synonym, taxon, false);
322 try {
323 this.dao.flush();
324 SynonymDeletionConfigurator config = new SynonymDeletionConfigurator();
325 config.setDeleteNameIfPossible(false);
326 this.deleteSynonym(synonym, acceptedTaxon, config);
327
328 } catch (Exception e) {
329 logger.info("Can't delete old synonym from database");
330 }
331 }
332
333 return newAcceptedTaxon;
334 }
335
336
337 @Override
338 public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
339
340 // Get name from synonym
341 TaxonNameBase<?, ?> synonymName = synonym.getName();
342
343 /* // remove synonym from taxon
344 toTaxon.removeSynonym(synonym);
345 */
346 // Create a taxon with synonym name
347 Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
348
349 // Add taxon relation
350 fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
351
352 // since we are swapping names, we have to detach the name from the synonym completely.
353 // Otherwise the synonym will still be in the list of typified names.
354 // synonym.getName().removeTaxonBase(synonym);
355 this.deleteSynonym(synonym, null);
356
357 return fromTaxon;
358 }
359
360
361 /* (non-Javadoc)
362 * @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)
363 */
364 @Transactional(readOnly = false)
365 @Override
366 public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
367 boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
368 // Get synonym name
369 TaxonNameBase synonymName = synonym.getName();
370 HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
371
372
373 // Switch groups
374 oldHomotypicalGroup.removeTypifiedName(synonymName);
375 newHomotypicalGroup.addTypifiedName(synonymName);
376
377 //remove existing basionym relationships
378 synonymName.removeBasionyms();
379
380 //add basionym relationship
381 if (setBasionymRelationIfApplicable){
382 Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
383 for (TaxonNameBase basionym : basionyms){
384 synonymName.addBasionym(basionym);
385 }
386 }
387
388 //set synonym relationship correctly
389 // SynonymRelationship relToTaxon = null;
390 boolean relToTargetTaxonExists = false;
391 Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
392 for (SynonymRelationship rel : existingRelations){
393 Taxon acceptedTaxon = rel.getAcceptedTaxon();
394 boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
395 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
396 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
397 SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
398 rel.setType(newRelationType);
399 //TODO handle citation and microCitation
400
401 if (isTargetTaxon){
402 relToTargetTaxonExists = true;
403 }else{
404 if (removeFromOtherTaxa){
405 acceptedTaxon.removeSynonym(synonym, false);
406 }else{
407 //do nothing
408 }
409 }
410 }
411 if (targetTaxon != null && ! relToTargetTaxonExists ){
412 Taxon acceptedTaxon = targetTaxon;
413 HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
414 boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
415 SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
416 //TODO handle citation and microCitation
417 Reference citation = null;
418 String microCitation = null;
419 acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
420 }
421
422 }
423
424
425 /* (non-Javadoc)
426 * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
427 */
428 @Override
429 @Transactional(readOnly = false)
430 public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
431 if (clazz == null){
432 clazz = TaxonBase.class;
433 }
434 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
435 }
436
437 @Override
438 @Autowired
439 protected void setDao(ITaxonDao dao) {
440 this.dao = dao;
441 }
442
443 /* (non-Javadoc)
444 * @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)
445 */
446 @Override
447 public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
448 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
449
450 List<TaxonBase> results = new ArrayList<TaxonBase>();
451 if(numberOfResults > 0) { // no point checking again
452 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
453 }
454
455 return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
456 }
457
458 /* (non-Javadoc)
459 * @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)
460 */
461 @Override
462 public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial, String infragenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
463 Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
464
465 List<TaxonBase> results = new ArrayList<TaxonBase>();
466 if(numberOfResults > 0) { // no point checking again
467 results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
468 }
469
470 return results;
471 }
472
473 /* (non-Javadoc)
474 * @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)
475 */
476 @Override
477 public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
478 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
479
480 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
481 if(numberOfResults > 0) { // no point checking again
482 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
483 }
484 return results;
485 }
486
487 /* (non-Javadoc)
488 * @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)
489 */
490 @Override
491 public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
492 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
493
494 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
495 if(numberOfResults > 0) { // no point checking again
496 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
497 }
498 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
499 }
500
501 /* (non-Javadoc)
502 * @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)
503 */
504 @Override
505 public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
506 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
507
508 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
509 if(numberOfResults > 0) { // no point checking again
510 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
511 }
512 return results;
513 }
514
515 /* (non-Javadoc)
516 * @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)
517 */
518 @Override
519 public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
520 Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
521
522 List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
523 if(numberOfResults > 0) { // no point checking again
524 results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
525 }
526 return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
527 }
528
529 @Override
530 public List<Taxon> listAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,
531 List<OrderHint> orderHints, List<String> propertyPaths){
532 return pageAcceptedTaxaFor(synonymUuid, classificationUuid, pageSize, pageNumber, orderHints, propertyPaths).getRecords();
533 }
534
535 @Override
536 public Pager<Taxon> pageAcceptedTaxaFor(UUID synonymUuid, UUID classificationUuid, Integer pageSize, Integer pageNumber,
537 List<OrderHint> orderHints, List<String> propertyPaths){
538
539 List<Taxon> list = new ArrayList<Taxon>();
540 Long count = 0l;
541
542 Synonym synonym = null;
543
544 try {
545 synonym = (Synonym) dao.load(synonymUuid);
546 } catch (ClassCastException e){
547 throw new EntityNotFoundException("The TaxonBase entity referenced by " + synonymUuid + " is not a Synonmy");
548 } catch (NullPointerException e){
549 throw new EntityNotFoundException("No TaxonBase entity found for " + synonymUuid);
550 }
551
552 Classification classificationFilter = null;
553 if(classificationUuid != null){
554 try {
555 classificationFilter = classificationDao.load(classificationUuid);
556 } catch (NullPointerException e){
557 throw new EntityNotFoundException("No Classification entity found for " + classificationUuid);
558 }
559 if(classificationFilter == null){
560
561 }
562 }
563
564 count = dao.countAcceptedTaxaFor(synonym, classificationFilter) ;
565 if(count > (pageSize * pageNumber)){
566 list = dao.listAcceptedTaxaFor(synonym, classificationFilter, pageSize, pageNumber, orderHints, propertyPaths);
567 }
568
569 return new DefaultPagerImpl<Taxon>(pageNumber, count.intValue(), pageSize, list);
570 }
571
572
573 /**
574 * @param taxon
575 * @param includeRelationships
576 * @param maxDepth
577 * @param limit
578 * @param starts
579 * @param propertyPaths
580 * @return an List which is not specifically ordered
581 */
582 @Override
583 public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,
584 Integer limit, Integer start, List<String> propertyPaths) {
585
586 Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<Taxon>(), maxDepth);
587 relatedTaxa.remove(taxon);
588 beanInitializer.initializeAll(relatedTaxa, propertyPaths);
589 return relatedTaxa;
590 }
591
592
593 /**
594 * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
595 * <code>taxon</code> supplied as parameter.
596 *
597 * @param taxon
598 * @param includeRelationships
599 * @param taxa
600 * @param maxDepth can be <code>null</code> for infinite depth
601 * @return
602 */
603 private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa, Integer maxDepth) {
604
605 if(taxa.isEmpty()) {
606 taxa.add(taxon);
607 }
608
609 if(includeRelationships.isEmpty()){
610 return taxa;
611 }
612
613 if(maxDepth != null) {
614 maxDepth--;
615 }
616 if(logger.isDebugEnabled()){
617 logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);
618 }
619 List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null);
620 for (TaxonRelationship taxRel : taxonRelationships) {
621
622 // skip invalid data
623 if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {
624 continue;
625 }
626 // filter by includeRelationships
627 for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {
628 if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) {
629 if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {
630 if(logger.isDebugEnabled()){
631 logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());
632 }
633 taxa.add(taxRel.getToTaxon());
634 if(maxDepth == null || maxDepth > 0) {
635 taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth));
636 }
637 }
638 if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {
639 taxa.add(taxRel.getFromTaxon());
640 if(logger.isDebugEnabled()){
641 logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );
642 }
643 if(maxDepth == null || maxDepth > 0) {
644 taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth));
645 }
646 }
647 }
648 }
649 }
650 return taxa;
651 }
652
653 /* (non-Javadoc)
654 * @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)
655 */
656 @Override
657 public Pager<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
658 Integer numberOfResults = dao.countSynonyms(taxon, type);
659
660 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
661 if(numberOfResults > 0) { // no point checking again
662 results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
663 }
664
665 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
666 }
667
668 /* (non-Javadoc)
669 * @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)
670 */
671 @Override
672 public Pager<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
673 Integer numberOfResults = dao.countSynonyms(synonym, type);
674
675 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
676 if(numberOfResults > 0) { // no point checking again
677 results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
678 }
679
680 return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
681 }
682
683 /* (non-Javadoc)
684 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
685 */
686 @Override
687 public List<List<Synonym>> getSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
688 List<List<Synonym>> result = new ArrayList<List<Synonym>>();
689 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
690
691 //homotypic
692 result.add(t.getHomotypicSynonymsByHomotypicGroup());
693
694 //heterotypic
695 List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
696 for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
697 result.add(t.getSynonymsInGroup(homotypicalGroup));
698 }
699
700 return result;
701
702 }
703
704 /* (non-Javadoc)
705 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
706 */
707 @Override
708 public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
709 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
710 return t.getHomotypicSynonymsByHomotypicGroup();
711 }
712
713 /* (non-Javadoc)
714 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
715 */
716 @Override
717 public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
718 Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
719 List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
720 List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
721 for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
722 heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
723 }
724 return heterotypicSynonymyGroups;
725 }
726
727 @Override
728 public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){
729
730 List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();
731 // Class<? extends TaxonBase> clazz = null;
732 // if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
733 // clazz = TaxonBase.class;
734 // //propertyPath.addAll(configurator.getTaxonPropertyPath());
735 // //propertyPath.addAll(configurator.getSynonymPropertyPath());
736 // } else if(configurator.isDoTaxa()) {
737 // clazz = Taxon.class;
738 // //propertyPath = configurator.getTaxonPropertyPath();
739 // } else if (configurator.isDoSynonyms()) {
740 // clazz = Synonym.class;
741 // //propertyPath = configurator.getSynonymPropertyPath();
742 // }
743
744
745 result = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
746 return result;
747 }
748
749 /* (non-Javadoc)
750 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
751 */
752 @Override
753 public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {
754
755 List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
756 int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
757 List<TaxonBase> taxa = null;
758
759 // Taxa and synonyms
760 long numberTaxaResults = 0L;
761
762
763 List<String> propertyPath = new ArrayList<String>();
764 if(configurator.getTaxonPropertyPath() != null){
765 propertyPath.addAll(configurator.getTaxonPropertyPath());
766 }
767
768
769 if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){
770 if(configurator.getPageSize() != null){ // no point counting if we need all anyway
771 numberTaxaResults =
772 dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
773 configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
774 configurator.getNamedAreas());
775 }
776
777 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
778 taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
779 configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),
780 configurator.getMatchMode(), configurator.getNamedAreas(),
781 configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
782 }
783 }
784
785 if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
786
787 if(taxa != null){
788 results.addAll(taxa);
789 }
790
791 numberOfResults += numberTaxaResults;
792
793 // Names without taxa
794 if (configurator.isDoNamesWithoutTaxa()) {
795 int numberNameResults = 0;
796
797 List<? extends TaxonNameBase<?,?>> names =
798 nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
799 configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
800 if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
801 if (names.size() > 0) {
802 for (TaxonNameBase<?,?> taxonName : names) {
803 if (taxonName.getTaxonBases().size() == 0) {
804 results.add(taxonName);
805 numberNameResults++;
806 }
807 }
808 if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
809 numberOfResults += numberNameResults;
810 }
811 }
812
813 // Taxa from common names
814
815 if (configurator.isDoTaxaByCommonNames()) {
816 taxa = new ArrayList<TaxonBase>();
817 numberTaxaResults = 0;
818 if(configurator.getPageSize() != null){// no point counting if we need all anyway
819 numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
820 }
821 if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
822 List<Object[]> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
823 for( Object[] entry : commonNameResults ) {
824 taxa.add((TaxonBase) entry[0]);
825 }
826 }
827 if(taxa != null){
828 results.addAll(taxa);
829 }
830 numberOfResults += numberTaxaResults;
831
832 }
833
834 return new DefaultPagerImpl<IdentifiableEntity>
835 (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
836 }
837
838 public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
839 return dao.getUuidAndTitleCache();
840 }
841
842 /* (non-Javadoc)
843 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
844 */
845 @Override
846 public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
847 List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
848 taxon = (Taxon)dao.load(taxon.getUuid());
849 Set<TaxonDescription> descriptions = taxon.getDescriptions();
850 for (TaxonDescription taxDesc: descriptions){
851 Set<DescriptionElementBase> elements = taxDesc.getElements();
852 for (DescriptionElementBase descElem: elements){
853 for(Media media : descElem.getMedia()){
854
855 //find the best matching representation
856 medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
857
858 }
859 }
860 }
861 return medRep;
862 }
863
864 /* (non-Javadoc)
865 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
866 */
867 @Override
868 public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){
869 return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);
870 }
871
872
873 /* (non-Javadoc)
874 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
875 */
876 @Override
877 public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
878 Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,
879 Boolean includeTaxonNameDescriptions, List<String> propertyPath) {
880
881 logger.trace("listMedia() - START");
882
883 Set<Taxon> taxa = new HashSet<Taxon>();
884 List<Media> taxonMedia = new ArrayList<Media>();
885
886 if (limitToGalleries == null) {
887 limitToGalleries = false;
888 }
889
890 // --- resolve related taxa
891 if (includeRelationships != null && ! includeRelationships.isEmpty()) {
892 logger.trace("listMedia() - resolve related taxa");
893 taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null);
894 }
895
896 taxa.add((Taxon) dao.load(taxon.getUuid()));
897
898 if(includeTaxonDescriptions != null && includeTaxonDescriptions){
899 logger.trace("listMedia() - includeTaxonDescriptions");
900 List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();
901 // --- TaxonDescriptions
902 for (Taxon t : taxa) {
903 taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));
904 }
905 for (TaxonDescription taxonDescription : taxonDescriptions) {
906 if (!limitToGalleries || taxonDescription.isImageGallery()) {
907 for (DescriptionElementBase element : taxonDescription.getElements()) {
908 for (Media media : element.getMedia()) {
909 taxonMedia.add(media);
910 }
911 }
912 }
913 }
914 }
915
916
917 if(includeOccurrences != null && includeOccurrences) {
918 logger.trace("listMedia() - includeOccurrences");
919 Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<SpecimenOrObservationBase>();
920 // --- Specimens
921 for (Taxon t : taxa) {
922 specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));
923 }
924 for (SpecimenOrObservationBase occurrence : specimensOrObservations) {
925
926 // direct media removed from specimen #3597
927 // taxonMedia.addAll(occurrence.getMedia());
928
929 // SpecimenDescriptions
930 Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();
931 for (DescriptionBase specimenDescription : specimenDescriptions) {
932 if (!limitToGalleries || specimenDescription.isImageGallery()) {
933 Set<DescriptionElementBase> elements = specimenDescription.getElements();
934 for (DescriptionElementBase element : elements) {
935 for (Media media : element.getMedia()) {
936 taxonMedia.add(media);
937 }
938 }
939 }
940 }
941
942 // Collection
943 //TODO why may collections have media attached? #
944 if (occurrence.isInstanceOf(DerivedUnit.class)) {
945 DerivedUnit derivedUnit = CdmBase.deproxy(occurrence, DerivedUnit.class);
946 if (derivedUnit.getCollection() != null){
947 taxonMedia.addAll(derivedUnit.getCollection().getMedia());
948 }
949 }
950
951 // pherograms & gelPhotos
952 if (occurrence.isInstanceOf(DnaSample.class)) {
953 DnaSample dnaSample = CdmBase.deproxy(occurrence, DnaSample.class);
954 Set<Sequence> sequences = dnaSample.getSequences();
955 //we do show only those gelPhotos which lead to a consensus sequence
956 for (Sequence sequence : sequences) {
957 Set<Media> dnaRelatedMedia = new HashSet<Media>();
958 for (SingleRead singleRead : sequence.getSingleReads()){
959 Amplification amplification = singleRead.getAmplification();
960 dnaRelatedMedia.add(amplification.getGelPhoto());
961 dnaRelatedMedia.add(singleRead.getPherogram());
962 dnaRelatedMedia.remove(null);
963 }
964 taxonMedia.addAll(dnaRelatedMedia);
965 }
966 }
967
968 }
969 }
970
971 if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {
972 logger.trace("listMedia() - includeTaxonNameDescriptions");
973 // --- TaxonNameDescription
974 Set<TaxonNameDescription> nameDescriptions = new HashSet<TaxonNameDescription>();
975 for (Taxon t : taxa) {
976 nameDescriptions .addAll(t.getName().getDescriptions());
977 }
978 for(TaxonNameDescription nameDescription: nameDescriptions){
979 if (!limitToGalleries || nameDescription.isImageGallery()) {
980 Set<DescriptionElementBase> elements = nameDescription.getElements();
981 for (DescriptionElementBase element : elements) {
982 for (Media media : element.getMedia()) {
983 taxonMedia.add(media);
984 }
985 }
986 }
987 }
988 }
989
990
991 logger.trace("listMedia() - initialize");
992 beanInitializer.initializeAll(taxonMedia, propertyPath);
993
994 logger.trace("listMedia() - END");
995
996 return taxonMedia;
997 }
998
999 /* (non-Javadoc)
1000 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
1001 */
1002 @Override
1003 public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
1004 return this.dao.listByIds(listOfIDs, null, null, null, null);
1005 }
1006
1007 /* (non-Javadoc)
1008 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
1009 */
1010 @Override
1011 public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
1012 return this.dao.findByUuid(uuid, null ,propertyPaths);
1013 }
1014
1015 /* (non-Javadoc)
1016 * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
1017 */
1018 @Override
1019 public int countAllRelationships() {
1020 return this.dao.countAllRelationships();
1021 }
1022
1023
1024
1025
1026 /* (non-Javadoc)
1027 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
1028 */
1029 @Override
1030 public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
1031 return this.dao.findIdenticalTaxonNames(propertyPath);
1032 }
1033
1034
1035 /* (non-Javadoc)
1036 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
1037 */
1038 @Override
1039 public String deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config, Classification classification) {
1040 if (config == null){
1041 config = new TaxonDeletionConfigurator();
1042 }
1043
1044 List<String> referencedObjects = isDeletable(taxon, config);
1045
1046 if (referencedObjects.isEmpty()){
1047 // --- DeleteSynonymRelations
1048 if (config.isDeleteSynonymRelations()){
1049 boolean removeSynonymNameFromHomotypicalGroup = false;
1050 // use tmp Set to avoid concurrent modification
1051 Set<SynonymRelationship> synRelsToDelete = new HashSet<SynonymRelationship>();
1052 synRelsToDelete.addAll(taxon.getSynonymRelations());
1053 for (SynonymRelationship synRel : synRelsToDelete){
1054 Synonym synonym = synRel.getSynonym();
1055 // taxon.removeSynonymRelation will set the accepted taxon and the synonym to NULL
1056 // this will cause hibernate to delete the relationship since
1057 // the SynonymRelationship field on both is annotated with removeOrphan
1058 // so no further explicit deleting of the relationship should be done here
1059 taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);
1060
1061 // --- DeleteSynonymsIfPossible
1062 if (config.isDeleteSynonymsIfPossible()){
1063 //TODO which value
1064 boolean newHomotypicGroupIfNeeded = true;
1065 SynonymDeletionConfigurator synConfig = new SynonymDeletionConfigurator();
1066 deleteSynonym(synonym, taxon, synConfig);
1067 }
1068 // relationship will be deleted by hibernate automatically,
1069 // see comment above and http://dev.e-taxonomy.eu/trac/ticket/3797
1070 // else{
1071 // deleteSynonymRelationships(synonym, taxon);
1072 // }
1073 }
1074 }
1075
1076 // --- DeleteTaxonRelationships
1077 if (! config.isDeleteTaxonRelationships()){
1078 if (taxon.getTaxonRelations().size() > 0){
1079 String message = "Taxon can't be deleted as it is related to another taxon. " +
1080 "Remove taxon from all relations to other taxa prior to deletion.";
1081 // throw new ReferencedObjectUndeletableException(message);
1082 }
1083 } else{
1084 for (TaxonRelationship taxRel: taxon.getTaxonRelations()){
1085 if (config.isDeleteMisappliedNamesAndInvalidDesignations()){
1086 if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR()) || taxRel.getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR())){
1087 if (taxon.equals(taxRel.getToTaxon())){
1088 this.deleteTaxon(taxRel.getFromTaxon(), config, classification);
1089 }
1090 }
1091 }
1092 taxon.removeTaxonRelation(taxRel);
1093 /*if (taxFrom.equals(taxon)){
1094 try{
1095 this.deleteTaxon(taxTo, taxConf, classification);
1096 } catch(DataChangeNoRollbackException e){
1097 logger.debug("A related taxon will not be deleted." + e.getMessage());
1098 }
1099 } else {
1100 try{
1101 this.deleteTaxon(taxFrom, taxConf, classification);
1102 } catch(DataChangeNoRollbackException e){
1103 logger.debug("A related taxon will not be deleted." + e.getMessage());
1104 }
1105
1106 }*/
1107 }
1108 }
1109
1110 // TaxonDescription
1111 if (config.isDeleteDescriptions()){
1112 Set<TaxonDescription> descriptions = taxon.getDescriptions();
1113 List<TaxonDescription> removeDescriptions = new ArrayList<TaxonDescription>();
1114 for (TaxonDescription desc: descriptions){
1115 //TODO use description delete configurator ?
1116 //FIXME check if description is ALWAYS deletable
1117 if (desc.getDescribedSpecimenOrObservation() != null){
1118 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1119 " which also describes specimens or abservations";
1120 //throw new ReferencedObjectUndeletableException(message);
1121 }
1122 removeDescriptions.add(desc);
1123 descriptionService.delete(desc);
1124
1125 }
1126 for (TaxonDescription desc: removeDescriptions){
1127 taxon.removeDescription(desc);
1128 }
1129 }
1130
1131
1132 /* //check references with only reverse mapping
1133 String message = checkForReferences(taxon);
1134 if (message != null){
1135 //throw new ReferencedObjectUndeletableException(message.toString());
1136 }*/
1137
1138 if (! config.isDeleteTaxonNodes() || (!config.isDeleteInAllClassifications() && classification == null )){
1139 //if (taxon.getTaxonNodes().size() > 0){
1140 // message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion or define a classification where it should be deleted or adapt the taxon deletion configurator.";
1141 // throw new ReferencedObjectUndeletableException(message);
1142 //}
1143 }else{
1144 if (taxon.getTaxonNodes().size() != 0){
1145 Set<TaxonNode> nodes = taxon.getTaxonNodes();
1146 Iterator<TaxonNode> iterator = nodes.iterator();
1147 TaxonNode node = null;
1148 boolean deleteChildren;
1149 if (config.getTaxonNodeConfig().getChildHandling().equals(ChildHandling.DELETE)){
1150 deleteChildren = true;
1151 }else {
1152 deleteChildren = false;
1153 }
1154 boolean success = true;
1155 if (!config.isDeleteInAllClassifications() && !(classification == null)){
1156 while (iterator.hasNext()){
1157 node = iterator.next();
1158 if (node.getClassification().equals(classification)){
1159 break;
1160 }
1161 node = null;
1162 }
1163 if (node != null){
1164 success =taxon.removeTaxonNode(node, deleteChildren);
1165 nodeService.delete(node);
1166 } else {
1167 // message = "Taxon is not used in defined classification";
1168 // throw new DataChangeNoRollbackException(message);
1169 }
1170 } else if (config.isDeleteInAllClassifications()){
1171 Set<ITaxonTreeNode> nodesList = new HashSet<ITaxonTreeNode>();
1172 nodesList.addAll(taxon.getTaxonNodes());
1173
1174 for (ITaxonTreeNode treeNode: nodesList){
1175 TaxonNode taxonNode = (TaxonNode) treeNode;
1176 if(!deleteChildren){
1177 /* Object[] childNodes = taxonNode.getChildNodes().toArray();
1178 //nodesList.addAll(taxonNode.getChildNodes());
1179 for (Object childNode: childNodes){
1180 TaxonNode childNodeCast = (TaxonNode) childNode;
1181 deleteTaxon(childNodeCast.getTaxon(), config, classification);
1182
1183 }
1184
1185 /*for (TaxonNode childNode: taxonNode.getChildNodes()){
1186 deleteTaxon(childNode.getTaxon(), config, classification);
1187
1188 }
1189 // taxon.removeTaxonNode(taxonNode);
1190 //nodeService.delete(taxonNode);
1191 } else{
1192 */
1193 Object[] childNodes = taxonNode.getChildNodes().toArray();
1194 for (Object childNode: childNodes){
1195 TaxonNode childNodeCast = (TaxonNode) childNode;
1196 taxonNode.getParent().addChildNode(childNodeCast, childNodeCast.getReference(), childNodeCast.getMicroReference());
1197 }
1198
1199 //taxon.removeTaxonNode(taxonNode);
1200 }
1201 }
1202 config.getTaxonNodeConfig().setDeleteTaxon(false);
1203 nodeService.deleteTaxonNodes(nodesList, config);
1204 }
1205 if (!success){
1206 // message = "The taxon node could not be deleted.";
1207 //throw new DataChangeNoRollbackException(message);
1208 }
1209 }
1210 }
1211
1212
1213 //PolytomousKey TODO
1214
1215 boolean usedInPolytomousKey = checkForPolytomousKeys(taxon);
1216 //TaxonNameBase
1217 if (config.isDeleteNameIfPossible()){
1218
1219
1220 //TaxonNameBase name = nameService.find(taxon.getName().getUuid());
1221 TaxonNameBase name = (TaxonNameBase)HibernateProxyHelper.deproxy(taxon.getName());
1222 //check whether taxon will be deleted or not
1223 if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) && name != null ){
1224 taxon = (Taxon) HibernateProxyHelper.deproxy(taxon);
1225 name.removeTaxonBase(taxon);
1226 nameService.save(name);
1227 String uuidString = nameService.delete(name, config.getNameDeletionConfig());
1228 logger.debug(uuidString);
1229 }
1230
1231 }
1232
1233 // TaxonDescription
1234 /* Set<TaxonDescription> descriptions = taxon.getDescriptions();
1235
1236 for (TaxonDescription desc: descriptions){
1237 if (config.isDeleteDescriptions()){
1238 //TODO use description delete configurator ?
1239 //FIXME check if description is ALWAYS deletable
1240 taxon.removeDescription(desc);
1241 descriptionService.delete(desc);
1242 }else{
1243 if (desc.getDescribedSpecimenOrObservations().size()>0){
1244 String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
1245 " which also describes specimens or observations";
1246 throw new ReferencedObjectUndeletableException(message);
1247 }
1248 }
1249 }*/
1250
1251 if ((taxon.getTaxonNodes() == null || taxon.getTaxonNodes().size()== 0) ){
1252 UUID uuid = dao.delete(taxon);
1253 return uuid.toString();
1254 } else {
1255 return "The Taxon can't be deleted.";
1256 }
1257 }else {
1258 return referencedObjects.toString();
1259 }
1260
1261
1262 }
1263
1264 private String checkForReferences(Taxon taxon){
1265 Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);
1266 for (CdmBase referencingObject : referencingObjects){
1267 //IIdentificationKeys (Media, Polytomous, MultiAccess)
1268 if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){
1269 String message = "Taxon" + taxon.getTitleCache() + "can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
1270
1271 return message;
1272 }
1273
1274
1275 /* //PolytomousKeyNode
1276 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
1277 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
1278 return message;
1279 }*/
1280
1281 //TaxonInteraction
1282 if (referencingObject.isInstanceOf(TaxonInteraction.class)){
1283 String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
1284 return message;
1285 }
1286
1287 //TaxonInteraction
1288 if (referencingObject.isInstanceOf(DeterminationEvent.class)){
1289 String message = "Taxon can't be deleted as it is used in a determination event";
1290 return message;
1291 }
1292
1293 }
1294
1295 referencingObjects = null;
1296 return null;
1297 }
1298
1299 private boolean checkForPolytomousKeys(Taxon taxon){
1300 boolean result = false;
1301 List<CdmBase> list = genericDao.getCdmBasesByFieldAndClass(PolytomousKeyNode.class, "taxon", taxon);
1302 if (!list.isEmpty()) {
1303 result = true;
1304 }
1305 return result;
1306 }
1307
1308 @Transactional(readOnly = false)
1309 public UUID delete(Synonym syn){
1310 UUID result = syn.getUuid();
1311 this.deleteSynonym(syn, null);
1312 return result;
1313 }
1314
1315 /* (non-Javadoc)
1316 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1317 */
1318 @Transactional(readOnly = false)
1319 @Override
1320 public String deleteSynonym(Synonym synonym, SynonymDeletionConfigurator config) {
1321 return deleteSynonym(synonym, null, config);
1322
1323 }
1324
1325
1326 /* (non-Javadoc)
1327 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1328 */
1329 @Transactional(readOnly = false)
1330 @Override
1331 public String deleteSynonym(Synonym synonym, Taxon taxon, SynonymDeletionConfigurator config) {
1332 if (synonym == null){
1333 return null;
1334 }
1335
1336 if (config == null){
1337 config = new SynonymDeletionConfigurator();
1338 }
1339 List<String> messages = isDeletable(synonym, config);
1340 if (messages.isEmpty()){
1341 synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
1342
1343 //remove synonymRelationship
1344 Set<Taxon> taxonSet = new HashSet<Taxon>();
1345 if (taxon != null){
1346 taxonSet.add(taxon);
1347 }else{
1348 taxonSet.addAll(synonym.getAcceptedTaxa());
1349 }
1350 for (Taxon relatedTaxon : taxonSet){
1351 // dao.deleteSynonymRelationships(synonym, relatedTaxon);
1352 relatedTaxon.removeSynonym(synonym, config.isNewHomotypicGroupIfNeeded());
1353 }
1354 this.saveOrUpdate(synonym);
1355
1356 //TODO remove name from homotypical group?
1357
1358 //remove synonym (if necessary)
1359
1360 UUID uuid = null;
1361 if (synonym.getSynonymRelations().isEmpty()){
1362 TaxonNameBase<?,?> name = synonym.getName();
1363 synonym.setName(null);
1364 uuid = dao.delete(synonym);
1365
1366 //remove name if possible (and required)
1367 if (name != null && config.isDeleteNameIfPossible()){
1368
1369 nameService.delete(name, config.getNameDeletionConfig());
1370
1371 }
1372
1373 }else {
1374 return null;
1375 }
1376 return uuid.toString();
1377 }else{
1378 return messages.toString();
1379 }
1380
1381
1382 }
1383
1384
1385 /* (non-Javadoc)
1386 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1387 */
1388 @Override
1389 public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
1390
1391 return this.dao.findIdenticalNamesNew(propertyPath);
1392 }
1393
1394 /* (non-Javadoc)
1395 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1396 */
1397 @Override
1398 public String getPhylumName(TaxonNameBase name){
1399 return this.dao.getPhylumName(name);
1400 }
1401
1402 /* (non-Javadoc)
1403 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1404 */
1405 @Override
1406 public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {
1407 return dao.deleteSynonymRelationships(syn, taxon);
1408 }
1409
1410 /* (non-Javadoc)
1411 * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1412 */
1413 @Override
1414 public long deleteSynonymRelationships(Synonym syn) {
1415 return dao.deleteSynonymRelationships(syn, null);
1416 }
1417
1418
1419 /* (non-Javadoc)
1420 * @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)
1421 */
1422 @Override
1423 public List<SynonymRelationship> listSynonymRelationships(
1424 TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1425 List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
1426 Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
1427
1428 List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
1429 if(numberOfResults > 0) { // no point checking again
1430 results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
1431 }
1432 return results;
1433 }
1434
1435 /* (non-Javadoc)
1436 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1437 */
1438 @Override
1439 public Taxon findBestMatchingTaxon(String taxonName) {
1440 MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
1441 config.setTaxonNameTitle(taxonName);
1442 return findBestMatchingTaxon(config);
1443 }
1444
1445
1446
1447 @Override
1448 public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
1449
1450 Taxon bestCandidate = null;
1451 try{
1452 // 1. search for acceptet taxa
1453 List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
1454 boolean bestCandidateMatchesSecUuid = false;
1455 boolean bestCandidateIsInClassification = false;
1456 int countEqualCandidates = 0;
1457 for(TaxonBase taxonBaseCandidate : taxonList){
1458 if(taxonBaseCandidate instanceof Taxon){
1459 Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
1460 boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
1461 if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
1462 continue;
1463 }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
1464 bestCandidate = newCanditate;
1465 countEqualCandidates = 1;
1466 bestCandidateMatchesSecUuid = true;
1467 continue;
1468 }
1469
1470 boolean newCandidateInClassification = isInClassification(newCanditate, config);
1471 if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
1472 continue;
1473 }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
1474 bestCandidate = newCanditate;
1475 countEqualCandidates = 1;
1476 bestCandidateIsInClassification = true;
1477 continue;
1478 }
1479 if (bestCandidate == null){
1480 bestCandidate = newCanditate;
1481 countEqualCandidates = 1;
1482 continue;
1483 }
1484
1485 }else{ //not Taxon.class
1486 continue;
1487 }
1488 countEqualCandidates++;
1489
1490 }
1491 if (bestCandidate != null){
1492 if(countEqualCandidates > 1){
1493 logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
1494 return bestCandidate;
1495 } else {
1496 logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
1497 return bestCandidate;
1498 }
1499 }
1500
1501
1502 // 2. search for synonyms
1503 if (config.isIncludeSynonyms()){
1504 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
1505 for(TaxonBase taxonBase : synonymList){
1506 if(taxonBase instanceof Synonym){
1507 Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
1508 Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
1509 if(!acceptetdCandidates.isEmpty()){
1510 bestCandidate = acceptetdCandidates.iterator().next();
1511 if(acceptetdCandidates.size() == 1){
1512 logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
1513 return bestCandidate;
1514 } else {
1515 logger.info("using accepted Taxon " + bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
1516 return bestCandidate;
1517 }
1518 //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1519 }
1520 }
1521 }
1522 }
1523
1524 } catch (Exception e){
1525 logger.error(e);
1526 e.printStackTrace();
1527 }
1528
1529 return bestCandidate;
1530 }
1531
1532 private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
1533 UUID configClassificationUuid = config.getClassificationUuid();
1534 if (configClassificationUuid == null){
1535 return false;
1536 }
1537 for (TaxonNode node : taxon.getTaxonNodes()){
1538 UUID classUuid = node.getClassification().getUuid();
1539 if (configClassificationUuid.equals(classUuid)){
1540 return true;
1541 }
1542 }
1543 return false;
1544 }
1545
1546 private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
1547 UUID configSecUuid = config.getSecUuid();
1548 if (configSecUuid == null){
1549 return false;
1550 }
1551 UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
1552 return configSecUuid.equals(taxonSecUuid);
1553 }
1554
1555 /* (non-Javadoc)
1556 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1557 */
1558 @Override
1559 public Synonym findBestMatchingSynonym(String taxonName) {
1560 List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
1561 if(! synonymList.isEmpty()){
1562 Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
1563 if(synonymList.size() == 1){
1564 logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
1565 return result;
1566 } else {
1567 logger.info("Several matching synonyms found. Using first: " + result.getTitleCache());
1568 return result;
1569 }
1570 }
1571 return null;
1572 }
1573
1574
1575 /* (non-Javadoc)
1576 * @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)
1577 */
1578 @Override
1579 public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
1580 SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
1581
1582 Synonym synonym = oldSynonymRelation.getSynonym();
1583 Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
1584 //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1585 TaxonNameBase<?,?> synonymName = synonym.getName();
1586 TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
1587 //set default relationship type
1588 if (newSynonymRelationshipType == null){
1589 newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
1590 }
1591 boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
1592
1593 HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
1594 int hgSize = homotypicGroup.getTypifiedNames().size();
1595 boolean isSingleInGroup = !(hgSize > 1);
1596
1597 if (! isSingleInGroup){
1598 boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
1599 boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
1600 if (isHomotypicToAccepted){
1601 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.";
1602 String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
1603 message = String.format(message, homotypicRelatives);
1604 throw new HomotypicalGroupChangeException(message);
1605 }
1606 if (! moveHomotypicGroup){
1607 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.";
1608 throw new HomotypicalGroupChangeException(message);
1609 }
1610 }else{
1611 moveHomotypicGroup = true; //single synonym always allows to moveCompleteGroup
1612 }
1613 // Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1614
1615 SynonymRelationship result = null;
1616 //move all synonyms to new taxon
1617 List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
1618 for (Synonym syn: homotypicSynonyms){
1619 Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
1620 for (SynonymRelationship synRelation : synRelations){
1621 if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
1622 Reference<?> newReference = reference;
1623 if (newReference == null && keepReference){
1624 newReference = synRelation.getCitation();
1625 }
1626 String newRefDetail = referenceDetail;
1627 if (newRefDetail == null && keepReference){
1628 newRefDetail = synRelation.getCitationMicroReference();
1629 }
1630 SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
1631 fromTaxon.removeSynonymRelation(synRelation, false);
1632 //
1633 //change homotypic group of synonym if relType is 'homotypic'
1634 // if (newRelTypeIsHomotypic){
1635 // newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1636 // }
1637 //set result
1638 if (synRelation.equals(oldSynonymRelation)){
1639 result = newSynRelation;
1640 }
1641 }
1642 }
1643
1644 }
1645 saveOrUpdate(newTaxon);
1646 //Assert that there is a result
1647 if (result == null){
1648 String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
1649 throw new IllegalStateException(message);
1650 }
1651 return result;
1652 }
1653
1654 /* (non-Javadoc)
1655 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1656 */
1657 @Override
1658 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1659 return dao.getUuidAndTitleCacheTaxon();
1660 }
1661
1662 /* (non-Javadoc)
1663 * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1664 */
1665 @Override
1666 public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
1667 return dao.getUuidAndTitleCacheSynonym();
1668 }
1669
1670 /* (non-Javadoc)
1671 * @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)
1672 */
1673 @Override
1674 public Pager<SearchResult<TaxonBase>> findByFullText(
1675 Class<? extends TaxonBase> clazz, String queryString,
1676 Classification classification, List<Language> languages,
1677 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1678
1679
1680 LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments, null);
1681
1682 // --- execute search
1683 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
1684
1685 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1686 idFieldMap.put(CdmBaseType.TAXON, "id");
1687
1688 // --- initialize taxa, thighlight matches ....
1689 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
1690 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1691 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
1692
1693 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
1694 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
1695 }
1696
1697 @Override
1698 public Pager<SearchResult<TaxonBase>> findByDistribution(List<NamedArea> areaFilter, List<PresenceAbsenceTermBase<?>> statusFilter,
1699 Classification classification,
1700 Integer pageSize, Integer pageNumber,
1701 List<OrderHint> orderHints, List<String> propertyPaths) throws IOException, ParseException {
1702
1703 LuceneSearch luceneSearch = prepareByDistributionSearch(areaFilter, statusFilter, classification);
1704
1705 // --- execute search
1706 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
1707
1708 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1709 idFieldMap.put(CdmBaseType.TAXON, "id");
1710
1711 // --- initialize taxa, thighlight matches ....
1712 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
1713 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1714 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
1715
1716 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
1717 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
1718 }
1719
1720 /**
1721 * @param clazz
1722 * @param queryString
1723 * @param classification
1724 * @param languages
1725 * @param highlightFragments
1726 * @param sortFields TODO
1727 * @param directorySelectClass
1728 * @return
1729 */
1730 protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,
1731 boolean highlightFragments, SortField[] sortFields) {
1732 BooleanQuery finalQuery = new BooleanQuery();
1733 BooleanQuery textQuery = new BooleanQuery();
1734
1735 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
1736 QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
1737
1738 if(sortFields == null){
1739 sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
1740 }
1741 luceneSearch.setSortFields(sortFields);
1742
1743 // ---- search criteria
1744 luceneSearch.setCdmTypRestriction(clazz);
1745
1746 textQuery.add(taxonBaseQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
1747 textQuery.add(taxonBaseQueryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);
1748
1749 finalQuery.add(textQuery, Occur.MUST);
1750
1751 if(classification != null){
1752 finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
1753 }
1754 luceneSearch.setQuery(finalQuery);
1755
1756 if(highlightFragments){
1757 luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
1758 }
1759 return luceneSearch;
1760 }
1761
1762 /**
1763 * Uses org.apache.lucene.search.join.JoinUtil for query time joining, alternatively
1764 * the BlockJoinQuery could be used. The latter might be more memory save but has the
1765 * drawback of requiring to do the join an indexing time.
1766 * see http://dev.e-taxonomy.eu/trac/wiki/LuceneNotes#JoinsinLucene for more information on this.
1767 *
1768 * Joins TaxonRelationShip with Taxon depending on the direction of the given edge:
1769 * <ul>
1770 * <li>direct, everted: {@link Direction.relatedTo}: TaxonRelationShip.relatedTo.id --&gt; Taxon.id </li>
1771 * <li>inverse: {@link Direction.relatedFrom}: TaxonRelationShip.relatedFrom.id --&gt; Taxon.id </li>
1772 * <ul>
1773 * @param queryString
1774 * @param classification
1775 * @param languages
1776 * @param highlightFragments
1777 * @param sortFields TODO
1778 *
1779 * @return
1780 * @throws IOException
1781 */
1782 protected LuceneSearch prepareFindByTaxonRelationFullTextSearch(TaxonRelationshipEdge edge, String queryString, Classification classification, List<Language> languages,
1783 boolean highlightFragments, SortField[] sortFields) throws IOException {
1784
1785 String fromField;
1786 String queryTermField;
1787 String toField = "id"; // TaxonBase.uuid
1788
1789 if(edge.isBidirectional()){
1790 throw new RuntimeException("Bidirectional joining not supported!");
1791 }
1792 if(edge.isEvers()){
1793 fromField = "relatedFrom.id";
1794 queryTermField = "relatedFrom.titleCache";
1795 } else if(edge.isInvers()) {
1796 fromField = "relatedTo.id";
1797 queryTermField = "relatedTo.titleCache";
1798 } else {
1799 throw new RuntimeException("Invalid direction: " + edge.getDirections());
1800 }
1801
1802 BooleanQuery finalQuery = new BooleanQuery();
1803
1804 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, TaxonBase.class);
1805 QueryFactory taxonBaseQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonBase.class);
1806
1807 BooleanQuery joinFromQuery = new BooleanQuery();
1808 joinFromQuery.add(taxonBaseQueryFactory.newTermQuery(queryTermField, queryString), Occur.MUST);
1809 joinFromQuery.add(taxonBaseQueryFactory.newEntityIdQuery("type.id", edge.getTaxonRelationshipType()), Occur.MUST);
1810 Query joinQuery = taxonBaseQueryFactory.newJoinQuery(fromField, toField, joinFromQuery, TaxonRelationship.class);
1811
1812 if(sortFields == null){
1813 sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
1814 }
1815 luceneSearch.setSortFields(sortFields);
1816
1817 finalQuery.add(joinQuery, Occur.MUST);
1818
1819 if(classification != null){
1820 finalQuery.add(taxonBaseQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
1821 }
1822 luceneSearch.setQuery(finalQuery);
1823
1824 if(highlightFragments){
1825 luceneSearch.setHighlightFields(taxonBaseQueryFactory.getTextFieldNamesAsArray());
1826 }
1827 return luceneSearch;
1828 }
1829
1830
1831
1832
1833 /* (non-Javadoc)
1834 * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNamesByFullText(java.util.EnumSet, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.Set, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.Map)
1835 */
1836 @Override
1837 public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(
1838 EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,
1839 Set<NamedArea> namedAreas, Set<PresenceAbsenceTermBase<?>> distributionStatus, List<Language> languages,
1840 boolean highlightFragments, Integer pageSize,
1841 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)
1842 throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {
1843
1844 // FIXME: allow taxonomic ordering
1845 // hql equivalent: order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
1846 // this require building a special sort column by a special classBridge
1847 if(highlightFragments){
1848 logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +
1849 "currently not fully supported by this method and thus " +
1850 "may not work with common names and misapplied names.");
1851 }
1852
1853 // convert sets to lists
1854 List<NamedArea> namedAreaList = null;
1855 List<PresenceAbsenceTermBase<?>>distributionStatusList = null;
1856 if(namedAreas != null){
1857 namedAreaList = new ArrayList<NamedArea>(namedAreas.size());
1858 namedAreaList.addAll(namedAreas);
1859 }
1860 if(distributionStatus != null){
1861 distributionStatusList = new ArrayList<PresenceAbsenceTermBase<?>>(distributionStatus.size());
1862 distributionStatusList.addAll(distributionStatus);
1863 }
1864
1865 // set default if parameter is null
1866 if(searchModes == null){
1867 searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);
1868 }
1869
1870 // set sort order and thus override any sort orders which may have been
1871 // defindes by prepare*Search methods
1872 if(orderHints == null){
1873 orderHints = OrderHint.NOMENCLATURAL_SORT_ORDER;
1874 }
1875 SortField[] sortFields = new SortField[orderHints.size()];
1876 int i = 0;
1877 for(OrderHint oh : orderHints){
1878 sortFields[i++] = oh.toSortField();
1879 }
1880 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};
1881 // SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};
1882
1883
1884 boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;
1885
1886 List<LuceneSearch> luceneSearches = new ArrayList<LuceneSearch>();
1887 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1888
1889 /*
1890 ======== filtering by distribution , HOWTO ========
1891
1892 - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html
1893 - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter
1894 add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html
1895 which will be put into a FilteredQuersy in the end ?
1896
1897
1898 3. how does it work in spatial?
1899 see
1900 - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html
1901 - http://www.infoq.com/articles/LuceneSpatialSupport
1902 - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html
1903 ------------------------------------------------------------------------
1904
1905 filter strategies:
1906 A) use a separate distribution filter per index sub-query/search:
1907 - byTaxonSyonym (query TaxaonBase):
1908 use a join area filter (Distribution -> TaxonBase)
1909 - byCommonName (query DescriptionElementBase): use an area filter on
1910 DescriptionElementBase !!! PROBLEM !!!
1911 This cannot work since the distributions are different entities than the
1912 common names and thus these are different lucene documents.
1913 - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):
1914 use a join area filter (Distribution -> TaxonBase)
1915
1916 B) use a common distribution filter for all index sub-query/searches:
1917 - use a common join area filter (Distribution -> TaxonBase)
1918 - also implement the byCommonName as join query (CommonName -> TaxonBase)
1919 PROBLEM in this case: we are losing the fragment highlighting for the
1920 common names, since the returned documents are always TaxonBases
1921 */
1922
1923 /* The QueryFactory for creating filter queries on Distributions should
1924 * The query factory used for the common names query cannot be reused
1925 * for this case, since we want to only record the text fields which are
1926 * actually used in the primary query
1927 */
1928 QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);
1929
1930 BooleanFilter multiIndexByAreaFilter = new BooleanFilter();
1931
1932
1933 // search for taxa or synonyms
1934 if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
1935 Class taxonBaseSubclass = TaxonBase.class;
1936 if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
1937 taxonBaseSubclass = Taxon.class;
1938 } else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {
1939 taxonBaseSubclass = Synonym.class;
1940 }
1941 luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments, sortFields));
1942 idFieldMap.put(CdmBaseType.TAXON, "id");
1943 /* A) does not work!!!!
1944 if(addDistributionFilter){
1945 // in this case we need a filter which uses a join query
1946 // to get the TaxonBase documents for the DescriptionElementBase documents
1947 // which are matching the areas in question
1948 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
1949 namedAreaList,
1950 distributionStatusList,
1951 distributionFilterQueryFactory
1952 );
1953 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1954 }
1955 */
1956 if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){
1957 // add additional area filter for synonyms
1958 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
1959 String toField = "accTaxon.id"; // id in TaxonBase index
1960
1961 BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
1962
1963 Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);
1964 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
1965
1966 }
1967 }
1968
1969 // search by CommonTaxonName
1970 if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {
1971 // B)
1972 QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
1973 Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(
1974 "inDescription.taxon.id",
1975 "id",
1976 QueryFactory.addTypeRestriction(
1977 createByDescriptionElementFullTextQuery(queryString, classification, null, languages, descriptionElementQueryFactory)
1978 , CommonTaxonName.class
1979 ),
1980 CommonTaxonName.class);
1981 logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());
1982 LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
1983 byCommonNameSearch.setCdmTypRestriction(Taxon.class);
1984 byCommonNameSearch.setQuery(byCommonNameJoinQuery);
1985 byCommonNameSearch.setSortFields(sortFields);
1986 idFieldMap.put(CdmBaseType.TAXON, "id");
1987
1988 luceneSearches.add(byCommonNameSearch);
1989
1990 /* A) does not work!!!!
1991 luceneSearches.add(
1992 prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,
1993 queryString, classification, null, languages, highlightFragments)
1994 );
1995 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1996 if(addDistributionFilter){
1997 // in this case we are able to use DescriptionElementBase documents
1998 // which are matching the areas in question directly
1999 BooleanQuery byDistributionQuery = createByDistributionQuery(
2000 namedAreaList,
2001 distributionStatusList,
2002 distributionFilterQueryFactory
2003 );
2004 multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);
2005 } */
2006 }
2007
2008 // search by misapplied names
2009 if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)) {
2010 // NOTE:
2011 // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()
2012 // which allows doing query time joins
2013 // finds the misapplied name (Taxon B) which is an misapplication for
2014 // a related Taxon A.
2015 //
2016 luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(
2017 new TaxonRelationshipEdge(TaxonRelationshipType.MISAPPLIED_NAME_FOR(), Direction.relatedTo),
2018 queryString, classification, languages, highlightFragments, sortFields));
2019 idFieldMap.put(CdmBaseType.TAXON, "id");
2020
2021 if(addDistributionFilter){
2022 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
2023
2024 /*
2025 * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.
2026 * Maybe this is a but in java itself java.
2027 *
2028 * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()
2029 * directly:
2030 *
2031 * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";
2032 *
2033 * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query
2034 * will execute as expected:
2035 *
2036 * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2037 * String toField = "relation." + misappliedNameForUuid +".to.id";
2038 *
2039 * Comparing both strings by the String.equals method returns true, so both String are identical.
2040 *
2041 * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be
2042 * dependent from a specific jvm (openjdk6 6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2, oracle jdk1.7.0_25 tested)
2043 * The bug is persistent after a reboot of the development computer.
2044 */
2045 // String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();
2046 // String toField = "relation." + misappliedNameForUuid +".to.id";
2047 String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";
2048 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");
2049 // System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");
2050
2051 BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);
2052 Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);
2053 QueryWrapperFilter filter = new QueryWrapperFilter(taxonAreaJoinQuery);
2054
2055 // debug code for bug described above
2056 DocIdSet filterMatchSet = filter.getDocIdSet(luceneIndexToolProvider.getIndexReaderFor(Taxon.class));
2057 // System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));
2058
2059 multiIndexByAreaFilter.add(filter, Occur.SHOULD);
2060 }
2061 }
2062
2063 LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,
2064 luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));
2065
2066
2067 if(addDistributionFilter){
2068
2069 // B)
2070 // in this case we need a filter which uses a join query
2071 // to get the TaxonBase documents for the DescriptionElementBase documents
2072 // which are matching the areas in question
2073 //
2074 // for toTaxa, doByCommonName
2075 Query taxonAreaJoinQuery = createByDistributionJoinQuery(
2076 namedAreaList,
2077 distributionStatusList,
2078 distributionFilterQueryFactory
2079 );
2080 multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);
2081 }
2082
2083 if (addDistributionFilter){
2084 multiSearch.setFilter(multiIndexByAreaFilter);
2085 }
2086
2087
2088 // --- execute search
2089 TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
2090
2091 // --- initialize taxa, highlight matches ....
2092 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
2093
2094
2095 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
2096 topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
2097
2098 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
2099 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
2100 }
2101
2102 /**
2103 * @param namedAreaList at least one area must be in the list
2104 * @param distributionStatusList optional
2105 * @return
2106 * @throws IOException
2107 */
2108 protected Query createByDistributionJoinQuery(
2109 List<NamedArea> namedAreaList,
2110 List<PresenceAbsenceTermBase<?>> distributionStatusList,
2111 QueryFactory queryFactory
2112 ) throws IOException {
2113
2114 String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index
2115 String toField = "id"; // id in TaxonBase index
2116
2117 BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);
2118
2119 Query taxonAreaJoinQuery = queryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);
2120
2121 return taxonAreaJoinQuery;
2122 }
2123
2124 /**
2125 * @param namedAreaList
2126 * @param distributionStatusList
2127 * @param queryFactory
2128 * @return
2129 */
2130 private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,
2131 List<PresenceAbsenceTermBase<?>> distributionStatusList, QueryFactory queryFactory) {
2132 BooleanQuery areaQuery = new BooleanQuery();
2133 // area field from Distribution
2134 areaQuery.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);
2135
2136 // status field from Distribution
2137 if(distributionStatusList != null && distributionStatusList.size() > 0){
2138 areaQuery.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);
2139 }
2140
2141 logger.debug("createByDistributionQuery() query: " + areaQuery.toString());
2142 return areaQuery;
2143 }
2144
2145 /**
2146 * This method has been primarily created for testing the area join query but might
2147 * also be useful in other situations
2148 *
2149 * @param namedAreaList
2150 * @param distributionStatusList
2151 * @param classification
2152 * @param highlightFragments
2153 * @return
2154 * @throws IOException
2155 */
2156 protected LuceneSearch prepareByDistributionSearch(
2157 List<NamedArea> namedAreaList, List<PresenceAbsenceTermBase<?>> distributionStatusList,
2158 Classification classification) throws IOException {
2159
2160 BooleanQuery finalQuery = new BooleanQuery();
2161
2162 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);
2163
2164 // FIXME is this query factory using the wrong type?
2165 QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);
2166
2167 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
2168 luceneSearch.setSortFields(sortFields);
2169
2170
2171 Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory);
2172
2173 finalQuery.add(byAreaQuery, Occur.MUST);
2174
2175 if(classification != null){
2176 finalQuery.add(taxonQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
2177 }
2178
2179 logger.info("prepareByAreaSearch() query: " + finalQuery.toString());
2180 luceneSearch.setQuery(finalQuery);
2181
2182 return luceneSearch;
2183 }
2184
2185
2186
2187 /* (non-Javadoc)
2188 * @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)
2189 */
2190 @Override
2191 public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
2192 Class<? extends DescriptionElementBase> clazz, String queryString,
2193 Classification classification, List<Feature> features, List<Language> languages,
2194 boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
2195
2196
2197 LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);
2198
2199 // --- execute search
2200 TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
2201
2202 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
2203 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2204
2205 // --- initialize taxa, highlight matches ....
2206 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
2207 @SuppressWarnings("rawtypes")
2208 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
2209 topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
2210
2211 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
2212 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
2213
2214 }
2215
2216
2217 @Override
2218 public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,
2219 Classification classification, List<Language> languages, boolean highlightFragments,
2220 Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {
2221
2222 LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);
2223 LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null);
2224
2225 LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);
2226
2227 // --- execute search
2228 TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
2229
2230 // --- initialize taxa, highlight matches ....
2231 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
2232
2233 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
2234 idFieldMap.put(CdmBaseType.TAXON, "id");
2235 idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
2236
2237 List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
2238 topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
2239
2240 int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
2241 return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
2242
2243 }
2244
2245
2246 /**
2247 * @param clazz
2248 * @param queryString
2249 * @param classification
2250 * @param features
2251 * @param languages
2252 * @param highlightFragments
2253 * @param directorySelectClass
2254 * @return
2255 */
2256 protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,
2257 String queryString, Classification classification, List<Feature> features,
2258 List<Language> languages, boolean highlightFragments) {
2259
2260 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);
2261 QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);
2262
2263 SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.STRING, false)};
2264
2265 BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, features,
2266 languages, descriptionElementQueryFactory);
2267
2268 luceneSearch.setSortFields(sortFields);
2269 luceneSearch.setCdmTypRestriction(clazz);
2270 luceneSearch.setQuery(finalQuery);
2271 if(highlightFragments){
2272 luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());
2273 }
2274
2275 return luceneSearch;
2276 }
2277
2278 /**
2279 * @param queryString
2280 * @param classification
2281 * @param features
2282 * @param languages
2283 * @param descriptionElementQueryFactory
2284 * @return
2285 */
2286 private BooleanQuery createByDescriptionElementFullTextQuery(String queryString, Classification classification,
2287 List<Feature> features, List<Language> languages, QueryFactory descriptionElementQueryFactory) {
2288 BooleanQuery finalQuery = new BooleanQuery();
2289 BooleanQuery textQuery = new BooleanQuery();
2290 textQuery.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
2291
2292 // common name
2293 Query nameQuery;
2294 if(languages == null || languages.size() == 0){
2295 nameQuery = descriptionElementQueryFactory.newTermQuery("name", queryString);
2296 } else {
2297 nameQuery = new BooleanQuery();
2298 BooleanQuery languageSubQuery = new BooleanQuery();
2299 for(Language lang : languages){
2300 languageSubQuery.add(descriptionElementQueryFactory.newTermQuery("language.uuid", lang.getUuid().toString(), false), Occur.SHOULD);
2301 }
2302 ((BooleanQuery) nameQuery).add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);
2303 ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);
2304 }
2305 textQuery.add(nameQuery, Occur.SHOULD);
2306
2307
2308 // text field from TextData
2309 textQuery.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);
2310
2311 // --- TermBase fields - by representation ----
2312 // state field from CategoricalData
2313 textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);
2314
2315 // state field from CategoricalData
2316 textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);
2317
2318 // area field from Distribution
2319 textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);
2320
2321 // status field from Distribution
2322 textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);
2323
2324 finalQuery.add(textQuery, Occur.MUST);
2325 // --- classification ----
2326
2327 if(classification != null){
2328 finalQuery.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
2329 }
2330
2331 // --- IdentifieableEntity fields - by uuid
2332 if(features != null && features.size() > 0 ){
2333 finalQuery.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);
2334 }
2335
2336 // the description must be associated with a taxon
2337 finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);
2338
2339 logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());
2340 return finalQuery;
2341 }
2342
2343 /**
2344 * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
2345 * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
2346 * This method is a convenient means to retrieve a Lucene query string for such the fields.
2347 *
2348 * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
2349 * or {@link MultilanguageTextFieldBridge }
2350 * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
2351 * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
2352 * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
2353 *
2354 * TODO move to utiliy class !!!!!!!!
2355 */
2356 private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
2357
2358 if(stringBuilder == null){
2359 stringBuilder = new StringBuilder();
2360 }
2361 if(languages == null || languages.size() == 0){
2362 stringBuilder.append(name + ".ALL:(%1$s) ");
2363 } else {
2364 for(Language lang : languages){
2365 stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
2366 }
2367 }
2368 return stringBuilder;
2369 }
2370
2371 @Override
2372 public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
2373 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
2374 List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
2375
2376 HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
2377
2378
2379 UUID nameUuid= taxon.getName().getUuid();
2380 ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);
2381 String epithetOfTaxon = null;
2382 String infragenericEpithetOfTaxon = null;
2383 String infraspecificEpithetOfTaxon = null;
2384 if (taxonName.isSpecies()){
2385 epithetOfTaxon= taxonName.getSpecificEpithet();
2386 } else if (taxonName.isInfraGeneric()){
2387 infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
2388 } else if (taxonName.isInfraSpecific()){
2389 infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
2390 }
2391 String genusOfTaxon = taxonName.getGenusOrUninomial();
2392 Set<TaxonNode> nodes = taxon.getTaxonNodes();
2393 List<String> taxonNames = new ArrayList<String>();
2394
2395 for (TaxonNode node: nodes){
2396 // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
2397 // List<String> synonymsEpithet = new ArrayList<String>();
2398
2399 if (node.getClassification().equals(classification)){
2400 if (!node.isTopmostNode()){
2401 TaxonNode parent = node.getParent();
2402 parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
2403 TaxonNameBase<?,?> parentName = parent.getTaxon().getName();
2404 ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
2405 Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
2406 Rank rankOfTaxon = taxonName.getRank();
2407
2408
2409 //create inferred synonyms for species, subspecies
2410 if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
2411
2412 Synonym inferredEpithet = null;
2413 Synonym inferredGenus = null;
2414 Synonym potentialCombination = null;
2415
2416 List<String> propertyPaths = new ArrayList<String>();
2417 propertyPaths.add("synonym");
2418 propertyPaths.add("synonym.name");
2419 List<OrderHint> orderHints = new ArrayList<OrderHint>();
2420 orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
2421
2422 List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
2423 List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
2424
2425 List<TaxonRelationship> taxonRelListParent = null;
2426 List<TaxonRelationship> taxonRelListTaxon = null;
2427 if (doWithMisappliedNames){
2428 taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
2429 taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
2430 }
2431
2432
2433 if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
2434
2435
2436 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
2437 Synonym syn = synonymRelationOfParent.getSynonym();
2438
2439 inferredEpithet = createInferredEpithets(taxon,
2440 zooHashMap, taxonName, epithetOfTaxon,
2441 infragenericEpithetOfTaxon,
2442 infraspecificEpithetOfTaxon,
2443 taxonNames, parentName,
2444 syn);
2445
2446
2447 inferredSynonyms.add(inferredEpithet);
2448 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
2449 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
2450 }
2451
2452 if (doWithMisappliedNames){
2453
2454 for (TaxonRelationship taxonRelationship: taxonRelListParent){
2455 Taxon misappliedName = taxonRelationship.getFromTaxon();
2456
2457 inferredEpithet = createInferredEpithets(taxon,
2458 zooHashMap, taxonName, epithetOfTaxon,
2459 infragenericEpithetOfTaxon,
2460 infraspecificEpithetOfTaxon,
2461 taxonNames, parentName,
2462 misappliedName);
2463
2464 inferredSynonyms.add(inferredEpithet);
2465 zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
2466 taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
2467 }
2468 }
2469
2470 if (!taxonNames.isEmpty()){
2471 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
2472 ZoologicalName name;
2473 if (!synNotInCDM.isEmpty()){
2474 inferredSynonymsToBeRemoved.clear();
2475
2476 for (Synonym syn :inferredSynonyms){
2477 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
2478 if (!synNotInCDM.contains(name.getNameCache())){
2479 inferredSynonymsToBeRemoved.add(syn);
2480 }
2481 }
2482
2483 // Remove identified Synonyms from inferredSynonyms
2484 for (Synonym synonym : inferredSynonymsToBeRemoved) {
2485 inferredSynonyms.remove(synonym);
2486 }
2487 }
2488 }
2489
2490 }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
2491
2492
2493 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
2494 TaxonNameBase synName;
2495 ZoologicalName inferredSynName;
2496
2497 Synonym syn = synonymRelationOfTaxon.getSynonym();
2498 inferredGenus = createInferredGenus(taxon,
2499 zooHashMap, taxonName, epithetOfTaxon,
2500 genusOfTaxon, taxonNames, zooParentName, syn);
2501
2502 inferredSynonyms.add(inferredGenus);
2503 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
2504 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
2505
2506
2507 }
2508
2509 if (doWithMisappliedNames){
2510
2511 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
2512 Taxon misappliedName = taxonRelationship.getFromTaxon();
2513 inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);
2514
2515 inferredSynonyms.add(inferredGenus);
2516 zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
2517 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
2518 }
2519 }
2520
2521
2522 if (!taxonNames.isEmpty()){
2523 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
2524 ZoologicalName name;
2525 if (!synNotInCDM.isEmpty()){
2526 inferredSynonymsToBeRemoved.clear();
2527
2528 for (Synonym syn :inferredSynonyms){
2529 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
2530 if (!synNotInCDM.contains(name.getNameCache())){
2531 inferredSynonymsToBeRemoved.add(syn);
2532 }
2533 }
2534
2535 // Remove identified Synonyms from inferredSynonyms
2536 for (Synonym synonym : inferredSynonymsToBeRemoved) {
2537 inferredSynonyms.remove(synonym);
2538 }
2539 }
2540 }
2541
2542 }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
2543
2544 Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
2545 ZoologicalName inferredSynName;
2546 //for all synonyms of the parent...
2547 for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
2548 TaxonNameBase synName;
2549 Synonym synParent = synonymRelationOfParent.getSynonym();
2550 synName = synParent.getName();
2551
2552 HibernateProxyHelper.deproxy(synParent);
2553
2554 // Set the sourceReference
2555 sourceReference = synParent.getSec();
2556
2557 // Determine the idInSource
2558 String idInSourceParent = getIdInSource(synParent);
2559
2560 ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
2561 String synParentGenus = parentSynZooName.getGenusOrUninomial();
2562 String synParentInfragenericName = null;
2563 String synParentSpecificEpithet = null;
2564
2565 if (parentSynZooName.isInfraGeneric()){
2566 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
2567 }
2568 if (parentSynZooName.isSpecies()){
2569 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
2570 }
2571
2572 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2573 synonymsGenus.put(synGenusName, idInSource);
2574 }*/
2575
2576 //for all synonyms of the taxon
2577
2578 for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
2579
2580 Synonym syn = synonymRelationOfTaxon.getSynonym();
2581 ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
2582 potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
2583 synParentGenus,
2584 synParentInfragenericName,
2585 synParentSpecificEpithet, syn, zooHashMap);
2586
2587 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
2588 inferredSynonyms.add(potentialCombination);
2589 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
2590 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
2591
2592 }
2593
2594
2595 }
2596
2597 if (doWithMisappliedNames){
2598
2599 for (TaxonRelationship parentRelationship: taxonRelListParent){
2600
2601 TaxonNameBase misappliedParentName;
2602
2603 Taxon misappliedParent = parentRelationship.getFromTaxon();
2604 misappliedParentName = misappliedParent.getName();
2605
2606 HibernateProxyHelper.deproxy(misappliedParent);
2607
2608 // Set the sourceReference
2609 sourceReference = misappliedParent.getSec();
2610
2611 // Determine the idInSource
2612 String idInSourceParent = getIdInSource(misappliedParent);
2613
2614 ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
2615 String synParentGenus = parentSynZooName.getGenusOrUninomial();
2616 String synParentInfragenericName = null;
2617 String synParentSpecificEpithet = null;
2618
2619 if (parentSynZooName.isInfraGeneric()){
2620 synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
2621 }
2622 if (parentSynZooName.isSpecies()){
2623 synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
2624 }
2625
2626
2627 for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
2628 Taxon misappliedName = taxonRelationship.getFromTaxon();
2629 ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
2630 potentialCombination = createPotentialCombination(
2631 idInSourceParent, parentSynZooName, zooMisappliedName,
2632 synParentGenus,
2633 synParentInfragenericName,
2634 synParentSpecificEpithet, misappliedName, zooHashMap);
2635
2636
2637 taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
2638 inferredSynonyms.add(potentialCombination);
2639 zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
2640 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
2641 }
2642 }
2643 }
2644
2645 if (!taxonNames.isEmpty()){
2646 List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
2647 ZoologicalName name;
2648 if (!synNotInCDM.isEmpty()){
2649 inferredSynonymsToBeRemoved.clear();
2650 for (Synonym syn :inferredSynonyms){
2651 try{
2652 name = (ZoologicalName) syn.getName();
2653 }catch (ClassCastException e){
2654 name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
2655 }
2656 if (!synNotInCDM.contains(name.getNameCache())){
2657 inferredSynonymsToBeRemoved.add(syn);
2658 }
2659 }
2660 // Remove identified Synonyms from inferredSynonyms
2661 for (Synonym synonym : inferredSynonymsToBeRemoved) {
2662 inferredSynonyms.remove(synonym);
2663 }
2664 }
2665 }
2666 }
2667 }else {
2668 logger.info("The synonymrelationship type is not defined.");
2669 return inferredSynonyms;
2670 }
2671 }
2672 }
2673
2674 }
2675
2676 return inferredSynonyms;
2677 }
2678
2679 private Synonym createPotentialCombination(String idInSourceParent,
2680 ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,
2681 String synParentInfragenericName, String synParentSpecificEpithet,
2682 TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
2683 Synonym potentialCombination;
2684 Reference sourceReference;
2685 ZoologicalName inferredSynName;
2686 HibernateProxyHelper.deproxy(syn);
2687
2688 // Set sourceReference
2689 sourceReference = syn.getSec();
2690 if (sourceReference == null){
2691 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2692 //TODO:Remove
2693 if (!parentSynZooName.getTaxa().isEmpty()){
2694 TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
2695
2696 sourceReference = taxon.getSec();
2697 }
2698 }
2699 String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
2700
2701 String synTaxonInfraSpecificName= null;
2702
2703 if (parentSynZooName.isSpecies()){
2704 synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
2705 }
2706
2707 /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
2708 synonymsEpithet.add(epithetName);
2709 }*/
2710
2711 //create potential combinations...
2712 inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
2713
2714 inferredSynName.setGenusOrUninomial(synParentGenus);
2715 if (zooSynName.isSpecies()){
2716 inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
2717 if (parentSynZooName.isInfraGeneric()){
2718 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
2719 }
2720 }
2721 if (zooSynName.isInfraSpecific()){
2722 inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
2723 inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
2724 }
2725 if (parentSynZooName.isInfraGeneric()){
2726 inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
2727 }
2728
2729
2730 potentialCombination = Synonym.NewInstance(inferredSynName, null);
2731
2732 // Set the sourceReference
2733 potentialCombination.setSec(sourceReference);
2734
2735
2736 // Determine the idInSource
2737 String idInSourceSyn= getIdInSource(syn);
2738
2739 if (idInSourceParent != null && idInSourceSyn != null) {
2740 IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
2741 inferredSynName.addSource(originalSource);
2742 originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
2743 potentialCombination.addSource(originalSource);
2744 }
2745
2746 inferredSynName.generateTitle();
2747
2748 return potentialCombination;
2749 }
2750
2751 private Synonym createInferredGenus(Taxon taxon,
2752 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
2753 String epithetOfTaxon, String genusOfTaxon,
2754 List<String> taxonNames, ZoologicalName zooParentName,
2755 TaxonBase syn) {
2756
2757 Synonym inferredGenus;
2758 TaxonNameBase synName;
2759 ZoologicalName inferredSynName;
2760 synName =syn.getName();
2761 HibernateProxyHelper.deproxy(syn);
2762
2763 // Determine the idInSource
2764 String idInSourceSyn = getIdInSource(syn);
2765 String idInSourceTaxon = getIdInSource(taxon);
2766 // Determine the sourceReference
2767 Reference sourceReference = syn.getSec();
2768
2769 //logger.warn(sourceReference.getTitleCache());
2770
2771 synName = syn.getName();
2772 ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
2773 String synSpeciesEpithetName = synZooName.getSpecificEpithet();
2774 /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
2775 synonymsEpithet.add(synSpeciesEpithetName);
2776 }*/
2777
2778 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
2779 //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...
2780
2781
2782 inferredSynName.setGenusOrUninomial(genusOfTaxon);
2783 if (zooParentName.isInfraGeneric()){
2784 inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
2785 }
2786
2787 if (taxonName.isSpecies()){
2788 inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
2789 }
2790 if (taxonName.isInfraSpecific()){
2791 inferredSynName.setSpecificEpithet(epithetOfTaxon);
2792 inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
2793 }
2794
2795
2796 inferredGenus = Synonym.NewInstance(inferredSynName, null);
2797
2798 // Set the sourceReference
2799 inferredGenus.setSec(sourceReference);
2800
2801 // Add the original source
2802 if (idInSourceSyn != null && idInSourceTaxon != null) {
2803 IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2804 idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2805 inferredGenus.addSource(originalSource);
2806
2807 originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2808 idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2809 inferredSynName.addSource(originalSource);
2810 originalSource = null;
2811
2812 }else{
2813 logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
2814 IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2815 idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2816 inferredGenus.addSource(originalSource);
2817
2818 originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2819 idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2820 inferredSynName.addSource(originalSource);
2821 originalSource = null;
2822 }
2823
2824 taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
2825
2826 inferredSynName.generateTitle();
2827
2828
2829 return inferredGenus;
2830 }
2831
2832 private Synonym createInferredEpithets(Taxon taxon,
2833 HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
2834 String epithetOfTaxon, String infragenericEpithetOfTaxon,
2835 String infraspecificEpithetOfTaxon, List<String> taxonNames,
2836 TaxonNameBase parentName, TaxonBase syn) {
2837
2838 Synonym inferredEpithet;
2839 TaxonNameBase<?,?> synName;
2840 ZoologicalName inferredSynName;
2841 HibernateProxyHelper.deproxy(syn);
2842
2843 // Determine the idInSource
2844 String idInSourceSyn = getIdInSource(syn);
2845 String idInSourceTaxon = getIdInSource(taxon);
2846 // Determine the sourceReference
2847 Reference<?> sourceReference = syn.getSec();
2848
2849 if (sourceReference == null){
2850 logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
2851 sourceReference = taxon.getSec();
2852 }
2853
2854 synName = syn.getName();
2855 ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
2856 String synGenusName = zooSynName.getGenusOrUninomial();
2857 String synInfraGenericEpithet = null;
2858 String synSpecificEpithet = null;
2859
2860 if (zooSynName.getInfraGenericEpithet() != null){
2861 synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
2862 }
2863
2864 if (zooSynName.isInfraSpecific()){
2865 synSpecificEpithet = zooSynName.getSpecificEpithet();
2866 }
2867
2868 /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2869 synonymsGenus.put(synGenusName, idInSource);
2870 }*/
2871
2872 inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
2873
2874 // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2875 if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
2876 logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
2877 }
2878 inferredSynName.setGenusOrUninomial(synGenusName);
2879
2880 if (parentName.isInfraGeneric()){
2881 inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
2882 }
2883 if (taxonName.isSpecies()){
2884 inferredSynName.setSpecificEpithet(epithetOfTaxon);
2885 }else if (taxonName.isInfraSpecific()){
2886 inferredSynName.setSpecificEpithet(synSpecificEpithet);
2887 inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
2888 }
2889
2890 inferredEpithet = Synonym.NewInstance(inferredSynName, null);
2891
2892 // Set the sourceReference
2893 inferredEpithet.setSec(sourceReference);
2894
2895 /* Add the original source
2896 if (idInSource != null) {
2897 IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2898
2899 // Add the citation
2900 Reference citation = getCitation(syn);
2901 if (citation != null) {
2902 originalSource.setCitation(citation);
2903 inferredEpithet.addSource(originalSource);
2904 }
2905 }*/
2906 String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
2907
2908
2909 IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2910 taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2911
2912 inferredEpithet.addSource(originalSource);
2913
2914 originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,
2915 taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2916
2917 inferredSynName.addSource(originalSource);
2918
2919
2920
2921 taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
2922
2923 inferredSynName.generateTitle();
2924 return inferredEpithet;
2925 }
2926
2927 /**
2928 * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2929 * Very likely only useful for createInferredSynonyms().
2930 * @param uuid
2931 * @param zooHashMap
2932 * @return
2933 */
2934 private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
2935 ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
2936 if (taxonName == null) {
2937 taxonName = zooHashMap.get(uuid);
2938 }
2939 return taxonName;
2940 }
2941
2942 /**
2943 * Returns the idInSource for a given Synonym.
2944 * @param syn
2945 */
2946 private String getIdInSource(TaxonBase taxonBase) {
2947 String idInSource = null;
2948 Set<IdentifiableSource> sources = taxonBase.getSources();
2949 if (sources.size() == 1) {
2950 IdentifiableSource source = sources.iterator().next();
2951 if (source != null) {
2952 idInSource = source.getIdInSource();
2953 }
2954 } else if (sources.size() > 1) {
2955 int count = 1;
2956 idInSource = "";
2957 for (IdentifiableSource source : sources) {
2958 idInSource += source.getIdInSource();
2959 if (count < sources.size()) {
2960 idInSource += "; ";
2961 }
2962 count++;
2963 }
2964 } else if (sources.size() == 0){
2965 logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
2966 }
2967
2968
2969 return idInSource;
2970 }
2971
2972
2973 /**
2974 * Returns the citation for a given Synonym.
2975 * @param syn
2976 */
2977 private Reference getCitation(Synonym syn) {
2978 Reference citation = null;
2979 Set<IdentifiableSource> sources = syn.getSources();
2980 if (sources.size() == 1) {
2981 IdentifiableSource source = sources.iterator().next();
2982 if (source != null) {
2983 citation = source.getCitation();
2984 }
2985 } else if (sources.size() > 1) {
2986 logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
2987 }
2988
2989 return citation;
2990 }
2991
2992 @Override
2993 public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
2994 List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
2995
2996 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
2997 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
2998 inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
2999
3000 return inferredSynonyms;
3001 }
3002
3003 /* (non-Javadoc)
3004 * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
3005 */
3006 @Override
3007 public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {
3008
3009 // TODO quickly implemented, create according dao !!!!
3010 Set<TaxonNode> nodes = new HashSet<TaxonNode>();
3011 Set<Classification> classifications = new HashSet<Classification>();
3012 List<Classification> list = new ArrayList<Classification>();
3013
3014 if (taxonBase == null) {
3015 return list;
3016 }
3017
3018 taxonBase = load(taxonBase.getUuid());
3019
3020 if (taxonBase instanceof Taxon) {
3021 nodes.addAll(((Taxon)taxonBase).getTaxonNodes());
3022 } else {
3023 for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {
3024 nodes.addAll(taxon.getTaxonNodes());
3025 }
3026 }
3027 for (TaxonNode node : nodes) {
3028 classifications.add(node.getClassification());
3029 }
3030 list.addAll(classifications);
3031 return list;
3032 }
3033
3034 @Override
3035 public Synonym changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,
3036 SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {
3037 // Create new synonym using concept name
3038 TaxonNameBase<?, ?> synonymName = fromTaxon.getName();
3039 Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());
3040
3041 // Remove concept relation from taxon
3042 toTaxon.removeTaxon(fromTaxon, oldRelationshipType);
3043
3044
3045
3046
3047 // Create a new synonym for the taxon
3048 SynonymRelationship synonymRelationship;
3049 if (synonymRelationshipType != null
3050 && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){
3051 synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null);
3052 } else{
3053 synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName);
3054 }
3055
3056 this.saveOrUpdate(toTaxon);
3057 //TODO: configurator and classification
3058 this.deleteTaxon(fromTaxon, null, null);
3059 return synonymRelationship.getSynonym();
3060
3061 }
3062 @Override
3063 public List<String> isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){
3064 List<String> result = new ArrayList<String>();
3065 Set<CdmBase> references = commonService.getReferencingObjects(taxonBase);
3066 if (taxonBase instanceof Taxon){
3067 TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;
3068 result = isDeletableForTaxon(references, taxonConfig);
3069 }else{
3070 SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;
3071 result = isDeletableForSynonym(references, synonymConfig);
3072 }
3073 return result;
3074 }
3075
3076 private List<String> isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){
3077 String message;
3078 List<String> result = new ArrayList<String>();
3079 for (CdmBase ref: references){
3080 if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase)){
3081 message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();
3082 result.add(message);
3083 }
3084 }
3085
3086 return result;
3087 }
3088 private List<String> isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){
3089 String message;
3090 List<String> result = new ArrayList<String>();
3091 for (CdmBase ref: references){
3092 if (!(ref instanceof TaxonNameBase)){
3093 if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){
3094 message = "The Taxon can't be deleted as long as it has synonyms.";
3095 result.add(message);
3096 }
3097 if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){
3098 message = "The Taxon can't be deleted as long as it has factual data.";
3099 result.add(message);
3100 }
3101
3102 if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){
3103 message = "The Taxon can't be deleted as long as it belongs to a taxon node.";
3104 result.add(message);
3105 }
3106 if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){
3107 if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){
3108 message = "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";
3109 result.add(message);
3110 } else{
3111 message = "The Taxon can't be deleted as long as it belongs to a taxon node.";
3112 result.add(message);
3113 }
3114 }
3115 if (ref instanceof PolytomousKeyNode){
3116 message = "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";
3117 result.add(message);
3118 }
3119
3120 if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){
3121 message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
3122 result.add(message);
3123
3124 }
3125
3126
3127 /* //PolytomousKeyNode
3128 if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
3129 String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";
3130 return message;
3131 }*/
3132
3133 //TaxonInteraction
3134 if (ref.isInstanceOf(TaxonInteraction.class)){
3135 message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
3136 result.add(message);
3137 }
3138
3139 //TaxonInteraction
3140 if (ref.isInstanceOf(DeterminationEvent.class)){
3141 message = "Taxon can't be deleted as it is used in a determination event";
3142 result.add(message);
3143 }
3144
3145 }
3146
3147 }
3148
3149 return result;
3150 }
3151
3152
3153
3154
3155
3156 }