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