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