Merge branch 'hotfix/3.12.3' into develop
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / NameServiceImpl.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.Collection;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.UUID;
22
23 import org.apache.log4j.Logger;
24 import org.apache.lucene.index.CorruptIndexException;
25 import org.apache.lucene.index.Term;
26 import org.apache.lucene.queryparser.classic.ParseException;
27 import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
28 import org.apache.lucene.search.BooleanClause.Occur;
29 import org.apache.lucene.search.BooleanQuery;
30 import org.apache.lucene.search.BooleanQuery.Builder;
31 import org.apache.lucene.search.TopDocs;
32 import org.apache.lucene.search.WildcardQuery;
33 import org.hibernate.criterion.Criterion;
34 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.beans.factory.annotation.Qualifier;
36 import org.springframework.stereotype.Service;
37 import org.springframework.transaction.annotation.Transactional;
38
39 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
40 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
41 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
42 import eu.etaxonomy.cdm.api.service.pager.Pager;
43 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
44 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
45 import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
46 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
47 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
48 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
49 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
50 import eu.etaxonomy.cdm.api.service.search.SearchResult;
51 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
52 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
53 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
54 import eu.etaxonomy.cdm.model.CdmBaseType;
55 import eu.etaxonomy.cdm.model.common.CdmBase;
56 import eu.etaxonomy.cdm.model.common.Language;
57 import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
58 import eu.etaxonomy.cdm.model.common.RelationshipBase;
59 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
60 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
61 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
62 import eu.etaxonomy.cdm.model.name.HybridRelationship;
63 import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
64 import eu.etaxonomy.cdm.model.name.NameRelationship;
65 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
66 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
67 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
68 import eu.etaxonomy.cdm.model.name.NonViralName;
69 import eu.etaxonomy.cdm.model.name.Rank;
70 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
71 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
72 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
73 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
74 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
75 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
76 import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
77 import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
78 import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
79 import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
80 import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
81 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
82 import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
83 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
84 import eu.etaxonomy.cdm.persistence.query.MatchMode;
85 import eu.etaxonomy.cdm.persistence.query.OrderHint;
86 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
87 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
88 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
89
90
91 @Service
92 @Transactional(readOnly = true)
93 public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxonNameDao> implements INameService {
94 static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
95
96 @Autowired
97 protected ITermVocabularyDao vocabularyDao;
98 @Autowired
99 protected IOrderedTermVocabularyDao orderedVocabularyDao;
100 @Autowired
101 @Qualifier("refEntDao")
102 protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
103 @Autowired
104 private INomenclaturalStatusDao nomStatusDao;
105 @Autowired
106 private ITypeDesignationDao typeDesignationDao;
107 @Autowired
108 private IHomotypicalGroupDao homotypicalGroupDao;
109 @Autowired
110 private ICdmGenericDao genericDao;
111 @Autowired
112 private ILuceneIndexToolProvider luceneIndexToolProvider;
113
114 /**
115 * Constructor
116 */
117 public NameServiceImpl(){
118 if (logger.isDebugEnabled()) { logger.debug("Load NameService Bean"); }
119 }
120
121 //********************* METHODS ****************************************************************//
122
123 /* (non-Javadoc)
124 * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
125 */
126 @Override
127 @Transactional(readOnly = false)
128 public DeleteResult delete(UUID nameUUID){
129 NameDeletionConfigurator config = new NameDeletionConfigurator();
130 DeleteResult result = delete(nameUUID, config);
131 return result;
132 }
133
134 @Override
135 public DeleteResult delete(TaxonNameBase name){
136 return delete(name.getUuid());
137 }
138
139 /* (non-Javadoc)
140 * @see eu.etaxonomy.cdm.api.service.INameService#delete(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.api.service.NameDeletionConfigurator)
141 */
142 @Override
143 @Transactional(readOnly = false)
144 public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
145 DeleteResult result = new DeleteResult();
146 TaxonNameBase name = dao.load(nameUUID);
147
148 if (name == null){
149 result.setAbort();
150 return result;
151 }
152
153 try{
154 result = this.isDeletable(name, config);
155 }catch(Exception e){
156 result.addException(e);
157 result.setError();
158 return result;
159 }
160 if (result.isOk()){
161 //remove references to this name
162 removeNameRelationshipsByDeleteConfig(name, config);
163
164 //remove name from homotypical group
165 HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
166 if (homotypicalGroup != null){
167 homotypicalGroup.removeTypifiedName(name, false);
168 }
169
170 //all type designation relationships are removed as they belong to the name
171 deleteTypeDesignation(name, null);
172 // //type designations
173 // if (! name.getTypeDesignations().isEmpty()){
174 // String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
175 // throw new ReferrencedObjectUndeletableException(message);
176 // }
177
178
179 try{
180 UUID nameUuid = dao.delete(name);
181
182 }catch(Exception e){
183 result.addException(e);
184 result.setError();
185 }
186 return result;
187 }
188
189
190
191 return result;
192 }
193
194 /* (non-Javadoc)
195 * @see eu.etaxonomy.cdm.api.service.INameService#deleteTypeDesignation(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.model.name.TypeDesignationBase)
196 */
197 @Override
198 public void deleteTypeDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation){
199 if (name == null && typeDesignation == null){
200 return;
201 }else if (name != null && typeDesignation != null){
202 removeSingleDesignation(name, typeDesignation);
203 }else if (name != null){
204 Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());
205 for (Object o : designationSet){
206 TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);
207 removeSingleDesignation(name, desig);
208 }
209 }else if (typeDesignation != null){
210 Set<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());
211 for (Object o : nameSet){
212 TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);
213 removeSingleDesignation(singleName, typeDesignation);
214 }
215 }
216 }
217
218 /**
219 * @param name
220 * @param typeDesignation
221 */
222 private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {
223 name.removeTypeDesignation(typeDesignation);
224 if (typeDesignation.getTypifiedNames().isEmpty()){
225 typeDesignation.removeType();
226 typeDesignationDao.delete(typeDesignation);
227 }
228 }
229
230
231
232 /**
233 * @param name
234 * @param config
235 */
236 private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {
237 try {
238 if (config.isRemoveAllNameRelationships()){
239 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
240 for (NameRelationship rel : rels){
241 name.removeNameRelationship(rel);
242 }
243 }else{
244 //relations to this name
245 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
246 for (NameRelationship rel : rels){
247 if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
248 name.removeNameRelationship(rel);
249 }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
250 name.removeNameRelationship(rel);
251 }
252 }
253 //relations from this name
254 rels = getModifiableSet(name.getRelationsFromThisName());
255 for (NameRelationship rel : rels){
256 if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType()) ){
257 name.removeNameRelationship(rel);
258 }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
259 name.removeNameRelationship(rel);
260 }
261 }
262
263 }
264 } catch (Exception e) {
265 throw new RuntimeException(e);
266 }
267 }
268
269 /**
270 * @param name
271 * @return
272 */
273 private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
274 Set<NameRelationship> rels = new HashSet<NameRelationship>();
275 for (NameRelationship rel : relations){
276 rels.add(rel);
277 }
278 return rels;
279 }
280
281 //********************* METHODS ****************************************************************//
282
283 /**
284 * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
285 * duplicate of findByName
286 */
287 @Override
288 @Deprecated
289 public List getNamesByName(String name){
290 return super.findCdmObjectsByTitle(name);
291 }
292
293 /**
294 * TODO candidate for harmonization
295 * new name findByName
296 */
297 @Override
298 public List<NonViralName> getNamesByNameCache(String nameCache){
299 List result = dao.findByName(nameCache, MatchMode.EXACT, null, null, null, null);
300 return result;
301 }
302
303
304 /**
305 * TODO candidate for harmonization
306 * new name saveHomotypicalGroups
307 *
308 * findByTitle
309 */
310 @Override
311 public List<NonViralName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
312 List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);
313 return result;
314 }
315
316 /**
317 * TODO candidate for harmonization
318 * new name saveHomotypicalGroups
319 *
320 * findByTitle
321 */
322 @Override
323 public List<NonViralName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
324 List result = dao.findByName(nameCache, matchMode, null, null, null ,propertyPaths);
325 return result;
326 }
327
328 /**
329 * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
330 * Replace by load(UUID, propertyPaths)
331 */
332 @Override
333 @Deprecated
334 public NonViralName findNameByUuid(UUID uuid, List<String> propertyPaths){
335 return (NonViralName)dao.findByUuid(uuid, null ,propertyPaths);
336 }
337
338 /**
339 * TODO candidate for harmonization
340 */
341 @Override
342 public List getNamesByName(String name, CdmBase sessionObject){
343 return super.findCdmObjectsByTitle(name, sessionObject);
344 }
345
346 /**
347 * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
348 * duplicate of findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths)
349 */
350 @Override
351 @Deprecated
352 public List findNamesByTitle(String title){
353 return super.findCdmObjectsByTitle(title);
354 }
355
356 /**
357 * @deprecated To be removed for harmonization see http://dev.e-taxonomy.eu/trac/wiki/CdmLibraryConventions
358 * duplicate of findByTitle()
359 */
360 @Override
361 @Deprecated
362 public List findNamesByTitle(String title, CdmBase sessionObject){
363 return super.findCdmObjectsByTitle(title, sessionObject);
364 }
365
366 /**
367 * TODO candidate for harmonization
368 * new name saveHomotypicalGroups
369 */
370 @Override
371 @Transactional(readOnly = false)
372 public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
373 return homotypicalGroupDao.saveAll(homotypicalGroups);
374 }
375
376 /**
377 * TODO candidate for harmonization
378 * new name saveTypeDesignations
379 */
380 @Override
381 @Transactional(readOnly = false)
382 public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
383 return typeDesignationDao.saveAll(typeDesignationCollection);
384 }
385
386 /**
387 * TODO candidate for harmonization
388 * new name saveReferencedEntities
389 */
390 @Override
391 @Transactional(readOnly = false)
392 public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
393 return referencedEntityDao.saveAll(referencedEntityCollection);
394 }
395
396 /**
397 * TODO candidate for harmonization
398 * new name getNames
399 */
400 public List<TaxonNameBase> getAllNames(int limit, int start){
401 return dao.list(limit, start);
402 }
403
404 /**
405 * TODO candidate for harmonization
406 * new name getNomenclaturalStatus
407 */
408 @Override
409 public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
410 return nomStatusDao.list(limit, start);
411 }
412
413 /**
414 * TODO candidate for harmonization
415 * new name getTypeDesignations
416 */
417 @Override
418 public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
419 return typeDesignationDao.getAllTypeDesignations(limit, start);
420 }
421 /**
422 * FIXME Candidate for harmonization
423 * homotypicalGroupService.list
424 */
425 @Override
426 public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
427 return homotypicalGroupDao.list(limit, start);
428 }
429
430 /**
431 * FIXME Candidate for harmonization
432 * remove
433 */
434 @Override
435 @Deprecated
436 public List<RelationshipBase> getAllRelationships(int limit, int start){
437 return dao.getAllRelationships(limit, start);
438 }
439
440
441
442 @Override
443 @Autowired
444 protected void setDao(ITaxonNameDao dao) {
445 this.dao = dao;
446 }
447
448 @Override
449 public Pager<HybridRelationship> getHybridNames(NonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
450 Integer numberOfResults = dao.countHybridNames(name, type);
451
452 List<HybridRelationship> results = new ArrayList<HybridRelationship>();
453 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
454 results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
455 }
456
457 return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
458 }
459
460 @Override
461 public List<NameRelationship> listNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
462 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
463
464 Integer numberOfResults = dao.countNameRelationships(name, direction, type);
465
466 List<NameRelationship> results = new ArrayList<NameRelationship>();
467 if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
468 results = dao.getNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
469 }
470 return results;
471 }
472
473
474 protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
475 NonViralName nvn,
476 float accuracy,
477 int maxNoOfResults,
478 List<Language> languages,
479 boolean highlightFragments) {
480 String similarity = Float.toString(accuracy);
481 String searchSuffix = "~" + similarity;
482
483
484 Builder finalQueryBuilder = new Builder();
485 finalQueryBuilder.setDisableCoord(false);
486 Builder textQueryBuilder = new Builder();
487 textQueryBuilder.setDisableCoord(false);
488
489 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
490 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
491
492 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
493 // luceneSearch.setSortFields(sortFields);
494
495 // ---- search criteria
496 luceneSearch.setCdmTypRestriction(clazz);
497
498 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
499 if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
500 fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
501 } else {
502 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
503 textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
504 }
505
506 if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
507 fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
508 } else {
509 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
510 textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
511 }
512
513 if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
514 fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
515 } else {
516 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
517 textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
518 }
519
520 if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
521 fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
522 } else {
523 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
524 textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
525 }
526
527 if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
528 fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
529 } else {
530 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
531 }
532
533 textQueryBuilder.add(fltq, Occur.MUST);
534
535 BooleanQuery textQuery = textQueryBuilder.build();
536 finalQueryBuilder.add(textQuery, Occur.MUST);
537
538 luceneSearch.setQuery(finalQueryBuilder.build());
539
540 if(highlightFragments){
541 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
542 }
543 return luceneSearch;
544 }
545
546 protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
547 String name,
548 float accuracy,
549 int maxNoOfResults,
550 List<Language> languages,
551 boolean highlightFragments) {
552
553 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
554 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
555
556 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
557 // luceneSearch.setSortFields(sortFields);
558
559 // ---- search criteria
560 luceneSearch.setCdmTypRestriction(clazz);
561 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
562
563 fltq.addTerms(name, "nameCache", accuracy, 3);
564
565 BooleanQuery finalQuery = new BooleanQuery(false);
566
567 finalQuery.add(fltq, Occur.MUST);
568
569 luceneSearch.setQuery(finalQuery);
570
571 if(highlightFragments){
572 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
573 }
574 return luceneSearch;
575 }
576
577 protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
578 String name,
579 boolean wildcard,
580 List<Language> languages,
581 boolean highlightFragments) {
582 Builder textQueryBuilder = new Builder();
583
584 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
585 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
586
587 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
588 // luceneSearch.setSortFields(sortFields);
589
590 // ---- search criteria
591 luceneSearch.setCdmTypRestriction(clazz);
592
593 if(name != null && !name.equals("")) {
594 if(wildcard) {
595 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
596 } else {
597 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
598 }
599 }
600
601 luceneSearch.setQuery(textQueryBuilder.build());
602
603 if(highlightFragments){
604 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
605 }
606 return luceneSearch;
607 }
608
609 @Override
610 public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(
611 String name,
612 float accuracy,
613 List<Language> languages,
614 boolean highlightFragments,
615 List<String> propertyPaths,
616 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
617
618 logger.info("Name to fuzzy search for : " + name);
619 // parse the input name
620 NonViralNameParserImpl parser = new NonViralNameParserImpl();
621 NonViralName nvn = parser.parseFullName(name);
622 if(name != null && !name.equals("") && nvn == null) {
623 throw new ParseException("Could not parse name " + name);
624 }
625 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
626
627 // --- execute search
628 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
629
630
631 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
632 idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
633
634 // --- initialize taxa, highlight matches ....
635 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
636
637 @SuppressWarnings("rawtypes")
638 List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(
639 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
640
641 return searchResults;
642
643 }
644
645 @Override
646 public List<DocumentSearchResult> findByNameFuzzySearch(
647 String name,
648 float accuracy,
649 List<Language> languages,
650 boolean highlightFragments,
651 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
652
653 logger.info("Name to fuzzy search for : " + name);
654 // parse the input name
655 NonViralNameParserImpl parser = new NonViralNameParserImpl();
656 NonViralName nvn = parser.parseFullName(name);
657 if(name != null && !name.equals("") && nvn == null) {
658 throw new ParseException("Could not parse name " + name);
659 }
660 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
661
662 // --- execute search
663 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
664
665 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
666
667 // --- initialize taxa, highlight matches ....
668 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
669
670 @SuppressWarnings("rawtypes")
671 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
672
673 return searchResults;
674 }
675
676 @Override
677 public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
678 String name,
679 float accuracy,
680 List<Language> languages,
681 boolean highlightFragments,
682 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
683
684 logger.info("Name to fuzzy search for : " + name);
685
686 LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
687
688 // --- execute search
689 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
690 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
691
692 // --- initialize taxa, highlight matches ....
693 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
694
695 @SuppressWarnings("rawtypes")
696 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
697
698 return searchResults;
699 }
700
701 @Override
702 public List<DocumentSearchResult> findByNameExactSearch(
703 String name,
704 boolean wildcard,
705 List<Language> languages,
706 boolean highlightFragments,
707 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
708
709 logger.info("Name to exact search for : " + name);
710
711 LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
712
713 // --- execute search
714
715
716 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
717
718 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
719
720 // --- initialize taxa, highlight matches ....
721 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
722
723 @SuppressWarnings("rawtypes")
724 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
725
726 return searchResults;
727 }
728
729 @Override
730 public Pager<NameRelationship> pageNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
731 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
732 List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
733 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
734 }
735
736 @Override
737 public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
738 return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
739 }
740
741 @Override
742 public Pager<NameRelationship> pageFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
743 List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
744 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
745 }
746
747 @Override
748 public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
749 return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
750 }
751
752 @Override
753 public Pager<NameRelationship> pageToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
754 List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
755 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
756 }
757
758 @Override
759 public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
760 Integer pageSize, Integer pageNumber) {
761 return getTypeDesignations(name, status, pageSize, pageNumber, null);
762 }
763
764 @Override
765 public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
766 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
767 Integer numberOfResults = dao.countTypeDesignations(name, status);
768
769 List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();
770 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {
771 results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
772 }
773
774 return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
775 }
776
777 /**
778 * FIXME Candidate for harmonization
779 * rename search
780 */
781 @Override
782 public Pager<TaxonNameBase> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
783 List<String> propertyPaths) {
784 Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
785
786 List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
787 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
788 results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
789 }
790
791 return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
792 }
793
794 @Override
795 public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames() {
796 return dao.getUuidAndTitleCacheOfNames();
797 }
798
799 @Override
800 public Pager<TaxonNameBase> findByName(Class<? extends TaxonNameBase> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
801 Integer numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
802
803 List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
804 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
805 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
806 }
807
808 return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
809 }
810
811 @Override
812 public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
813 return homotypicalGroupDao.findByUuid(uuid);
814 }
815
816 @Override
817 @Transactional(readOnly = false)
818 public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {
819 if (clazz == null){
820 clazz = TaxonNameBase.class;
821 }
822 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
823 }
824
825
826 @Override
827 protected void setOtherCachesNull(TaxonNameBase name) {
828 if (name.isInstanceOf(NonViralName.class)){
829 NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
830 if (! nvn.isProtectedNameCache()){
831 nvn.setNameCache(null, false);
832 }
833 if (! nvn.isProtectedAuthorshipCache()){
834 nvn.setAuthorshipCache(null, false);
835 }
836 if (! nvn.isProtectedFullTitleCache()){
837 nvn.setFullTitleCache(null, false);
838 }
839 }
840 }
841
842 @Override
843 public List<TaggedText> getTaggedName(UUID uuid) {
844 TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);
845 List<TaggedText> taggedName = taxonNameBase.getTaggedName();
846 return taggedName;
847 }
848
849 @Override
850 public DeleteResult isDeletable(TaxonNameBase name, DeleteConfiguratorBase config){
851 DeleteResult result = new DeleteResult();
852 //name = this.load(name.getUuid());
853 NameDeletionConfigurator nameConfig = null;
854 if (config instanceof NameDeletionConfigurator){
855 nameConfig = (NameDeletionConfigurator) config;
856 }else{
857 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
858 result.setError();
859 return result;
860 }
861
862 if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
863 HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
864
865 if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
866 result.addException(new Exception( "Name can't be deleted as it is a basionym."));
867 result.setAbort();
868 }
869 if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
870 result.addException(new Exception( "Name can't be deleted as it has a basionym."));
871 result.setAbort();
872 }
873 Set<NameRelationship> relationships = name.getNameRelations();
874 for (NameRelationship rel: relationships){
875 if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
876 result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
877 result.setAbort();
878 break;
879 }
880 }
881 }
882
883 //concepts
884 if (! name.getTaxonBases().isEmpty()){
885 result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
886 result.setAbort();
887 }
888
889 //hybrid relationships
890 if (name.isInstanceOf(NonViralName.class)){
891 NonViralName nvn = CdmBase.deproxy(name, NonViralName.class);
892 if (! nvn.getHybridParentRelations().isEmpty()){
893 result.addException(new Exception("Name can't be deleted as it is a parent in (a) hybrid relationship(s). Remove hybrid relationships prior to deletion."));
894 result.setAbort();
895 }
896 }
897 Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
898 for (CdmBase referencingObject : referencingObjects){
899 //DerivedUnit?.storedUnder
900 if (referencingObject.isInstanceOf(DerivedUnit.class)){
901 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
902 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
903 result.addException(new ReferencedObjectUndeletableException(message));
904 result.addRelatedObject(referencingObject);
905 result.setAbort();
906 }
907 //DescriptionElementSource#nameUsedInSource
908 else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
909 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
910 result.addException(new ReferencedObjectUndeletableException(message));
911 result.addRelatedObject(referencingObject);
912 result.setAbort();
913 }
914 //NameTypeDesignation#typeName
915 else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
916 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
917 result.addException(new ReferencedObjectUndeletableException(message));
918 result.addRelatedObject(referencingObject);
919 result.setAbort();
920 }
921 //DeterminationEvent#taxonName
922 else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
923 String message = "Name can't be deleted as it is used as a determination event";
924 result.addException(new ReferencedObjectUndeletableException(message));
925 result.addRelatedObject(referencingObject);
926 result.setAbort();
927 }
928 }
929
930 //TODO inline references
931
932
933 if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
934 String message = "Name can't be deleted as it is a replaced synonym.";
935 result.addException(new Exception(message));
936 result.setAbort();
937 }
938 if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
939 String message = "Name can't be deleted as it has a replaced synonym.";
940 result.addException(new Exception(message));
941 result.setAbort();
942 }
943 return result;
944
945 }
946
947 @Override
948 @Transactional(readOnly = true)
949 public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
950 TaxonNameBase name = dao.load(nameUuid);
951 UpdateResult result = new UpdateResult();
952 name.setAsGroupsBasionym();
953 result.addUpdatedObject(name);
954 return result;
955
956 }
957
958 @Override
959 public List<HashMap<String,String>> getNameRecords(){
960
961 return dao.getNameRecords();
962
963 }
964
965 }