ref #6362, ref #6365 remove occurrence of NonViralName, BotanicalName, ZoologicalName...
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / NameServiceImpl.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.api.service;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.Collection;
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.index.Term;
25 import org.apache.lucene.queryparser.classic.ParseException;
26 import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
27 import org.apache.lucene.search.BooleanClause.Occur;
28 import org.apache.lucene.search.BooleanQuery;
29 import org.apache.lucene.search.BooleanQuery.Builder;
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.INonViralName;
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.getUuid(), 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 return result;
190 }
191
192 @Override
193 public DeleteResult deleteTypeDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation){
194 if(typeDesignation!=null && typeDesignation.getId()!=0){
195 typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
196 }
197
198 DeleteResult result = new DeleteResult();
199 if (name == null && typeDesignation == null){
200 result.setError();
201 return result;
202 }else if (name != null && typeDesignation != null){
203 removeSingleDesignation(name, typeDesignation);
204 }else if (name != null){
205 Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());
206 for (Object o : designationSet){
207 TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);
208 removeSingleDesignation(name, desig);
209 }
210 }else if (typeDesignation != null){
211 Set<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());
212 for (Object o : nameSet){
213 TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);
214 removeSingleDesignation(singleName, typeDesignation);
215 }
216 }
217 result.addUpdatedObject(name);
218 return result;
219 }
220
221
222 @Override
223 public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
224 TaxonNameBase nameBase = load(nameUuid);
225 TypeDesignationBase typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
226 return deleteTypeDesignation(nameBase, typeDesignation);
227 }
228
229 /**
230 * @param name
231 * @param typeDesignation
232 */
233 private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {
234 name.removeTypeDesignation(typeDesignation);
235 if (typeDesignation.getTypifiedNames().isEmpty()){
236 typeDesignation.removeType();
237 typeDesignationDao.delete(typeDesignation);
238 }
239 }
240
241
242
243 /**
244 * @param name
245 * @param config
246 */
247 private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {
248 try {
249 if (config.isRemoveAllNameRelationships()){
250 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
251 for (NameRelationship rel : rels){
252 name.removeNameRelationship(rel);
253 }
254 }else{
255 //relations to this name
256 Set<NameRelationship> rels = getModifiableSet(name.getRelationsToThisName());
257 for (NameRelationship rel : rels){
258 if (config.isIgnoreHasBasionym() && NameRelationshipType.BASIONYM().equals(rel.getType() )){
259 name.removeNameRelationship(rel);
260 }else if (config.isIgnoreHasReplacedSynonym() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
261 name.removeNameRelationship(rel);
262 }
263 }
264 //relations from this name
265 rels = getModifiableSet(name.getRelationsFromThisName());
266 for (NameRelationship rel : rels){
267 if (config.isIgnoreIsBasionymFor() && NameRelationshipType.BASIONYM().equals(rel.getType()) ){
268 name.removeNameRelationship(rel);
269 }else if (config.isIgnoreIsReplacedSynonymFor() && NameRelationshipType.REPLACED_SYNONYM().equals(rel.getType())){
270 name.removeNameRelationship(rel);
271 }
272 }
273
274 }
275 } catch (Exception e) {
276 throw new RuntimeException(e);
277 }
278 }
279
280 /**
281 * @param name
282 * @return
283 */
284 private Set<NameRelationship> getModifiableSet(Set<NameRelationship> relations) {
285 Set<NameRelationship> rels = new HashSet<NameRelationship>();
286 for (NameRelationship rel : relations){
287 rels.add(rel);
288 }
289 return rels;
290 }
291
292 //********************* METHODS ****************************************************************//
293
294
295 /**
296 * TODO candidate for harmonization
297 * new name findByName
298 */
299 @Override
300 @Deprecated
301 public List<NonViralName> getNamesByNameCache(String nameCache){
302 boolean includeAuthors = false;
303 List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
304 return result;
305 }
306
307
308 /**
309 * TODO candidate for harmonization
310 * new name saveHomotypicalGroups
311 *
312 * findByTitle
313 */
314 @Override
315 public List<NonViralName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
316 List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);
317 return result;
318 }
319
320 /**
321 * TODO candidate for harmonization
322 * new name saveHomotypicalGroups
323 *
324 * findByTitle
325 */
326 @Override
327 public List<NonViralName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
328 List result = dao.findByName(false, nameCache, matchMode, null, null, null ,propertyPaths);
329 return result;
330 }
331
332 /**
333 * TODO candidate for harmonization
334 */
335 @Override
336 public List getNamesByName(String name, CdmBase sessionObject){
337 return super.findCdmObjectsByTitle(name, sessionObject);
338 }
339
340 /**
341 * TODO candidate for harmonization
342 * new name saveHomotypicalGroups
343 */
344 @Override
345 @Transactional(readOnly = false)
346 public Map<UUID, HomotypicalGroup> saveAllHomotypicalGroups(Collection<HomotypicalGroup> homotypicalGroups){
347 return homotypicalGroupDao.saveAll(homotypicalGroups);
348 }
349
350 /**
351 * TODO candidate for harmonization
352 * new name saveTypeDesignations
353 */
354 @Override
355 @Transactional(readOnly = false)
356 public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
357 return typeDesignationDao.saveAll(typeDesignationCollection);
358 }
359
360 /**
361 * TODO candidate for harmonization
362 * new name saveReferencedEntities
363 */
364 @Override
365 @Transactional(readOnly = false)
366 public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
367 return referencedEntityDao.saveAll(referencedEntityCollection);
368 }
369
370 /**
371 * TODO candidate for harmonization
372 * new name getNames
373 */
374 public List<TaxonNameBase> getAllNames(int limit, int start){
375 return dao.list(limit, start);
376 }
377
378 /**
379 * TODO candidate for harmonization
380 * new name getNomenclaturalStatus
381 */
382 @Override
383 public List<NomenclaturalStatus> getAllNomenclaturalStatus(int limit, int start){
384 return nomStatusDao.list(limit, start);
385 }
386
387 /**
388 * TODO candidate for harmonization
389 * new name getTypeDesignations
390 */
391 @Override
392 public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
393 return typeDesignationDao.getAllTypeDesignations(limit, start);
394 }
395 /**
396 * FIXME Candidate for harmonization
397 * homotypicalGroupService.list
398 */
399 @Override
400 public List<HomotypicalGroup> getAllHomotypicalGroups(int limit, int start){
401 return homotypicalGroupDao.list(limit, start);
402 }
403
404 /**
405 * FIXME Candidate for harmonization
406 * remove
407 */
408 @Override
409 @Deprecated
410 public List<RelationshipBase> getAllRelationships(int limit, int start){
411 return dao.getAllRelationships(limit, start);
412 }
413
414
415
416 @Override
417 @Autowired
418 protected void setDao(ITaxonNameDao dao) {
419 this.dao = dao;
420 }
421
422 @Override
423 public Pager<HybridRelationship> getHybridNames(INonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
424 Integer numberOfResults = dao.countHybridNames(name, type);
425
426 List<HybridRelationship> results = new ArrayList<HybridRelationship>();
427 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
428 results = dao.getHybridNames(name, type, pageSize, pageNumber,orderHints,propertyPaths);
429 }
430
431 return new DefaultPagerImpl<HybridRelationship>(pageNumber, numberOfResults, pageSize, results);
432 }
433
434 @Override
435 public List<NameRelationship> listNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
436 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
437
438 Integer numberOfResults = dao.countNameRelationships(name, direction, type);
439
440 List<NameRelationship> results = new ArrayList<NameRelationship>();
441 if (AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) { // no point checking again
442 results = dao.getNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
443 }
444 return results;
445 }
446
447
448 protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
449 INonViralName nvn,
450 float accuracy,
451 int maxNoOfResults,
452 List<Language> languages,
453 boolean highlightFragments) {
454 String similarity = Float.toString(accuracy);
455 String searchSuffix = "~" + similarity;
456
457
458 Builder finalQueryBuilder = new Builder();
459 finalQueryBuilder.setDisableCoord(false);
460 Builder textQueryBuilder = new Builder();
461 textQueryBuilder.setDisableCoord(false);
462
463 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
464 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
465
466 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
467 // luceneSearch.setSortFields(sortFields);
468
469 // ---- search criteria
470 luceneSearch.setCdmTypRestriction(clazz);
471
472 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
473 if(nvn.getGenusOrUninomial() != null && !nvn.getGenusOrUninomial().equals("")) {
474 fltq.addTerms(nvn.getGenusOrUninomial().toLowerCase(), "genusOrUninomial", accuracy, 3);
475 } else {
476 //textQuery.add(new RegexQuery (new Term ("genusOrUninomial", "^[a-zA-Z]*")), Occur.MUST_NOT);
477 textQueryBuilder.add(queryFactory.newTermQuery("genusOrUninomial", "_null_", false), Occur.MUST);
478 }
479
480 if(nvn.getInfraGenericEpithet() != null && !nvn.getInfraGenericEpithet().equals("")){
481 fltq.addTerms(nvn.getInfraGenericEpithet().toLowerCase(), "infraGenericEpithet", accuracy, 3);
482 } else {
483 //textQuery.add(new RegexQuery (new Term ("infraGenericEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
484 textQueryBuilder.add(queryFactory.newTermQuery("infraGenericEpithet", "_null_", false), Occur.MUST);
485 }
486
487 if(nvn.getSpecificEpithet() != null && !nvn.getSpecificEpithet().equals("")){
488 fltq.addTerms(nvn.getSpecificEpithet().toLowerCase(), "specificEpithet", accuracy, 3);
489 } else {
490 //textQuery.add(new RegexQuery (new Term ("specificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
491 textQueryBuilder.add(queryFactory.newTermQuery("specificEpithet", "_null_", false), Occur.MUST);
492 }
493
494 if(nvn.getInfraSpecificEpithet() != null && !nvn.getInfraSpecificEpithet().equals("")){
495 fltq.addTerms(nvn.getInfraSpecificEpithet().toLowerCase(), "infraSpecificEpithet", accuracy, 3);
496 } else {
497 //textQuery.add(new RegexQuery (new Term ("infraSpecificEpithet", "^[a-zA-Z]*")), Occur.MUST_NOT);
498 textQueryBuilder.add(queryFactory.newTermQuery("infraSpecificEpithet", "_null_", false), Occur.MUST);
499 }
500
501 if(nvn.getAuthorshipCache() != null && !nvn.getAuthorshipCache().equals("")){
502 fltq.addTerms(nvn.getAuthorshipCache().toLowerCase(), "authorshipCache", accuracy, 3);
503 } else {
504 //textQuery.add(new RegexQuery (new Term ("authorshipCache", "^[a-zA-Z]*")), Occur.MUST_NOT);
505 }
506
507 textQueryBuilder.add(fltq, Occur.MUST);
508
509 BooleanQuery textQuery = textQueryBuilder.build();
510 finalQueryBuilder.add(textQuery, Occur.MUST);
511
512 luceneSearch.setQuery(finalQueryBuilder.build());
513
514 if(highlightFragments){
515 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
516 }
517 return luceneSearch;
518 }
519
520 protected LuceneSearch prepareFindByFuzzyNameCacheSearch(Class<? extends CdmBase> clazz,
521 String name,
522 float accuracy,
523 int maxNoOfResults,
524 List<Language> languages,
525 boolean highlightFragments) {
526
527 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
528 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
529
530 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
531 // luceneSearch.setSortFields(sortFields);
532
533 // ---- search criteria
534 luceneSearch.setCdmTypRestriction(clazz);
535 FuzzyLikeThisQuery fltq = new FuzzyLikeThisQuery(maxNoOfResults, luceneSearch.getAnalyzer());
536
537 fltq.addTerms(name, "nameCache", accuracy, 3);
538
539 BooleanQuery finalQuery = new BooleanQuery(false);
540
541 finalQuery.add(fltq, Occur.MUST);
542
543 luceneSearch.setQuery(finalQuery);
544
545 if(highlightFragments){
546 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
547 }
548 return luceneSearch;
549 }
550
551 protected LuceneSearch prepareFindByExactNameSearch(Class<? extends CdmBase> clazz,
552 String name,
553 boolean wildcard,
554 List<Language> languages,
555 boolean highlightFragments) {
556 Builder textQueryBuilder = new Builder();
557
558 LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
559 QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
560
561 // SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};
562 // luceneSearch.setSortFields(sortFields);
563
564 // ---- search criteria
565 luceneSearch.setCdmTypRestriction(clazz);
566
567 if(name != null && !name.equals("")) {
568 if(wildcard) {
569 textQueryBuilder.add(new WildcardQuery(new Term("nameCache", name + "*")), Occur.MUST);
570 } else {
571 textQueryBuilder.add(queryFactory.newTermQuery("nameCache", name, false), Occur.MUST);
572 }
573 }
574
575 luceneSearch.setQuery(textQueryBuilder.build());
576
577 if(highlightFragments){
578 luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
579 }
580 return luceneSearch;
581 }
582
583 @Override
584 public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(
585 String name,
586 float accuracy,
587 List<Language> languages,
588 boolean highlightFragments,
589 List<String> propertyPaths,
590 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
591
592 logger.info("Name to fuzzy search for : " + name);
593 // parse the input name
594 NonViralNameParserImpl parser = new NonViralNameParserImpl();
595 INonViralName nvn = parser.parseFullName(name);
596 if(name != null && !name.equals("") && nvn == null) {
597 throw new ParseException("Could not parse name " + name);
598 }
599 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
600
601 // --- execute search
602 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
603
604
605 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
606 idFieldMap.put(CdmBaseType.NONVIRALNAME, "id");
607
608 // --- initialize taxa, highlight matches ....
609 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
610
611 @SuppressWarnings("rawtypes")
612 List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(
613 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
614
615 return searchResults;
616
617 }
618
619 @Override
620 public List<DocumentSearchResult> findByNameFuzzySearch(
621 String name,
622 float accuracy,
623 List<Language> languages,
624 boolean highlightFragments,
625 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
626
627 logger.info("Name to fuzzy search for : " + name);
628 // parse the input name
629 NonViralNameParserImpl parser = new NonViralNameParserImpl();
630 INonViralName nvn = parser.parseFullName(name);
631 if(name != null && !name.equals("") && nvn == null) {
632 throw new ParseException("Could not parse name " + name);
633 }
634 LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
635
636 // --- execute search
637 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
638
639 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
640
641 // --- initialize taxa, highlight matches ....
642 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
643
644 @SuppressWarnings("rawtypes")
645 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
646
647 return searchResults;
648 }
649
650 @Override
651 public List<DocumentSearchResult> findByFuzzyNameCacheSearch(
652 String name,
653 float accuracy,
654 List<Language> languages,
655 boolean highlightFragments,
656 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
657
658 logger.info("Name to fuzzy search for : " + name);
659
660 LuceneSearch luceneSearch = prepareFindByFuzzyNameCacheSearch(null, name, accuracy, maxNoOfResults, languages, highlightFragments);
661
662 // --- execute search
663 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
664 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
665
666 // --- initialize taxa, highlight matches ....
667 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
668
669 @SuppressWarnings("rawtypes")
670 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
671
672 return searchResults;
673 }
674
675 @Override
676 public List<DocumentSearchResult> findByNameExactSearch(
677 String name,
678 boolean wildcard,
679 List<Language> languages,
680 boolean highlightFragments,
681 int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
682
683 logger.info("Name to exact search for : " + name);
684
685 LuceneSearch luceneSearch = prepareFindByExactNameSearch(null, name, wildcard, languages, highlightFragments);
686
687 // --- execute search
688
689
690 TopDocs topDocs = luceneSearch.executeSearch(maxNoOfResults);
691
692 Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
693
694 // --- initialize taxa, highlight matches ....
695 ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
696
697 @SuppressWarnings("rawtypes")
698 List<DocumentSearchResult> searchResults = searchResultBuilder.createResultSet(topDocs, luceneSearch.getHighlightFields());
699
700 return searchResults;
701 }
702
703 @Override
704 public Pager<NameRelationship> pageNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
705 Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
706 List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
707 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
708 }
709
710 @Override
711 public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
712 return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
713 }
714
715 @Override
716 public Pager<NameRelationship> pageFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
717 List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
718 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
719 }
720
721 @Override
722 public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
723 return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
724 }
725
726 @Override
727 public Pager<NameRelationship> pageToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
728 List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
729 return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
730 }
731
732 @Override
733 public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
734 Integer pageSize, Integer pageNumber) {
735 return getTypeDesignations(name, status, pageSize, pageNumber, null);
736 }
737
738 @Override
739 public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
740 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
741 Integer numberOfResults = dao.countTypeDesignations(name, status);
742
743 List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();
744 if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {
745 results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
746 }
747
748 return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
749 }
750
751 /**
752 * FIXME Candidate for harmonization
753 * rename search
754 */
755 @Override
756 public Pager<TaxonNameBase> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
757 List<String> propertyPaths) {
758 Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
759
760 List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
761 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
762 results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
763 }
764
765 return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
766 }
767
768 @Override
769 public List<UuidAndTitleCache> getUuidAndTitleCacheOfNames(Integer limit, String pattern) {
770 return dao.getUuidAndTitleCacheOfNames(limit, pattern);
771 }
772
773 @Override
774 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) {
775 Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
776
777 List<TaxonNameBase> results = new ArrayList<>();
778 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
779 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
780 }
781
782 return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
783 }
784
785 @Override
786 public HomotypicalGroup findHomotypicalGroup(UUID uuid) {
787 return homotypicalGroupDao.findByUuid(uuid);
788 }
789
790 @Override
791 @Transactional(readOnly = false)
792 public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {
793 if (clazz == null){
794 clazz = TaxonNameBase.class;
795 }
796 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
797 }
798
799
800 @Override
801 protected void setOtherCachesNull(TaxonNameBase name) {
802 if (! name.isProtectedNameCache()){
803 name.setNameCache(null, false);
804 }
805 if (! name.isProtectedAuthorshipCache()){
806 name.setAuthorshipCache(null, false);
807 }
808 if (! name.isProtectedFullTitleCache()){
809 name.setFullTitleCache(null, false);
810 }
811
812 }
813
814 @Override
815 public List<TaggedText> getTaggedName(UUID uuid) {
816 TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);
817 List<TaggedText> taggedName = taxonNameBase.getTaggedName();
818 return taggedName;
819 }
820
821 @Override
822 public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
823 DeleteResult result = new DeleteResult();
824 TaxonNameBase name = this.load(nameUUID);
825 NameDeletionConfigurator nameConfig = null;
826 if (config instanceof NameDeletionConfigurator){
827 nameConfig = (NameDeletionConfigurator) config;
828 }else{
829 result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
830 result.setError();
831 return result;
832 }
833
834 if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
835 HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
836
837 if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
838 result.addException(new Exception( "Name can't be deleted as it is a basionym."));
839 result.setAbort();
840 }
841 if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
842 result.addException(new Exception( "Name can't be deleted as it has a basionym."));
843 result.setAbort();
844 }
845 Set<NameRelationship> relationships = name.getNameRelations();
846 for (NameRelationship rel: relationships){
847 if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
848 result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
849 result.setAbort();
850 break;
851 }
852 }
853 }
854
855 //concepts
856 if (! name.getTaxonBases().isEmpty()){
857 result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
858 result.setAbort();
859 }
860
861 //hybrid relationships
862 if (name.isInstanceOf(NonViralName.class)){
863 INonViralName nvn = name;
864 Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
865 //Hibernate.initialize(parentHybridRelations);
866 if (! parentHybridRelations.isEmpty()){
867 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."));
868 result.setAbort();
869 }
870 }
871 Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
872 for (CdmBase referencingObject : referencingObjects){
873 //DerivedUnit?.storedUnder
874 if (referencingObject.isInstanceOf(DerivedUnit.class)){
875 String message = "Name can't be deleted as it is used as derivedUnit#storedUnder by %s. Remove 'stored under' prior to deleting this name";
876 message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnit.class).getTitleCache());
877 result.addException(new ReferencedObjectUndeletableException(message));
878 result.addRelatedObject(referencingObject);
879 result.setAbort();
880 }
881 //DescriptionElementSource#nameUsedInSource
882 else if (referencingObject.isInstanceOf(DescriptionElementSource.class)){
883 String message = "Name can't be deleted as it is used as descriptionElementSource#nameUsedInSource";
884 result.addException(new ReferencedObjectUndeletableException(message));
885 result.addRelatedObject(referencingObject);
886 result.setAbort();
887 }
888 //NameTypeDesignation#typeName
889 else if (referencingObject.isInstanceOf(NameTypeDesignation.class)){
890 NameTypeDesignation typeDesignation = HibernateProxyHelper.deproxy(referencingObject, NameTypeDesignation.class);
891
892 if (typeDesignation.getTypeName().equals(name)){
893 String message = "Name can't be deleted as it is used as a name type in a NameTypeDesignation";
894 result.addException(new ReferencedObjectUndeletableException(message));
895 result.addRelatedObject(referencingObject);
896 result.setAbort();
897 }
898 }
899 //DeterminationEvent#taxonName
900 else if (referencingObject.isInstanceOf(DeterminationEvent.class)){
901 String message = "Name can't be deleted as it is used as a determination event";
902 result.addException(new ReferencedObjectUndeletableException(message));
903 result.addRelatedObject(referencingObject);
904 result.setAbort();
905 }
906 }
907
908 //TODO inline references
909
910
911 if (!nameConfig.isIgnoreIsReplacedSynonymFor() && name.isReplacedSynonym()){
912 String message = "Name can't be deleted as it is a replaced synonym.";
913 result.addException(new Exception(message));
914 result.setAbort();
915 }
916 if (!nameConfig.isIgnoreHasReplacedSynonym() && (name.getReplacedSynonyms().size()>0)){
917 String message = "Name can't be deleted as it has a replaced synonym.";
918 result.addException(new Exception(message));
919 result.setAbort();
920 }
921 return result;
922
923 }
924
925 @Override
926 @Transactional(readOnly = true)
927 public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
928 TaxonNameBase name = dao.load(nameUuid);
929 UpdateResult result = new UpdateResult();
930 name.setAsGroupsBasionym();
931 result.addUpdatedObject(name);
932 return result;
933
934 }
935
936 @Override
937 public List<HashMap<String,String>> getNameRecords(){
938
939 return dao.getNameRecords();
940
941 }
942
943 }