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