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