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