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