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