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