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