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
index 1c8de6e108fac14946ed034845de7066ce118abb..8f671809852afe20582fbeedaca4e641cc8117fb 100644 (file)
@@ -1,4 +1,3 @@
-// $Id$
 /**
 * Copyright (C) 2007 EDIT
 * European Distributed Institute of Taxonomy
@@ -12,18 +11,18 @@ package eu.etaxonomy.cdm.api.service;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 
 import org.apache.log4j.Logger;
-import org.apache.lucene.index.CorruptIndexException;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.queryparser.classic.ParseException;
 import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
@@ -38,6 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
 import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
+import eu.etaxonomy.cdm.api.service.dto.TypeDesignationStatusFilter;
 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
 import eu.etaxonomy.cdm.api.service.pager.Pager;
 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
@@ -45,10 +45,12 @@ import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
 import eu.etaxonomy.cdm.api.service.search.DocumentSearchResult;
 import eu.etaxonomy.cdm.api.service.search.ILuceneIndexToolProvider;
 import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
+import eu.etaxonomy.cdm.api.service.search.LuceneParseException;
 import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
 import eu.etaxonomy.cdm.api.service.search.QueryFactory;
 import eu.etaxonomy.cdm.api.service.search.SearchResult;
 import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
+import eu.etaxonomy.cdm.api.utility.TaxonNamePartsFilter;
 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.CdmBaseType;
@@ -57,29 +59,40 @@ import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
 import eu.etaxonomy.cdm.model.common.RelationshipBase;
 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
+import eu.etaxonomy.cdm.model.common.SourcedEntityBase;
 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
 import eu.etaxonomy.cdm.model.name.HybridRelationship;
 import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
+import eu.etaxonomy.cdm.model.name.INonViralName;
 import eu.etaxonomy.cdm.model.name.NameRelationship;
 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
-import eu.etaxonomy.cdm.model.name.NonViralName;
 import eu.etaxonomy.cdm.model.name.Rank;
+import eu.etaxonomy.cdm.model.name.Registration;
+import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.name.TaxonName;
 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
+import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
-import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
 import eu.etaxonomy.cdm.persistence.dao.common.IReferencedEntityDao;
-import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
+import eu.etaxonomy.cdm.persistence.dao.common.ISourcedEntityDao;
+import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
+import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
 import eu.etaxonomy.cdm.persistence.dao.name.IHomotypicalGroupDao;
 import eu.etaxonomy.cdm.persistence.dao.name.INomenclaturalStatusDao;
 import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
 import eu.etaxonomy.cdm.persistence.dao.name.ITypeDesignationDao;
+import eu.etaxonomy.cdm.persistence.dao.term.IOrderedTermVocabularyDao;
+import eu.etaxonomy.cdm.persistence.dao.term.ITermVocabularyDao;
+import eu.etaxonomy.cdm.persistence.dto.TaxonNameParts;
 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
 import eu.etaxonomy.cdm.persistence.query.MatchMode;
 import eu.etaxonomy.cdm.persistence.query.OrderHint;
@@ -90,7 +103,9 @@ import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
 
 @Service
 @Transactional(readOnly = true)
-public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxonNameDao> implements INameService {
+public class NameServiceImpl
+          extends IdentifiableServiceBase<TaxonName,ITaxonNameDao>
+          implements INameService {
     static private final Logger logger = Logger.getLogger(NameServiceImpl.class);
 
     @Autowired
@@ -98,9 +113,16 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     @Autowired
     protected IOrderedTermVocabularyDao orderedVocabularyDao;
     @Autowired
+    protected IOccurrenceService occurrenceService;
+    @Autowired
+    protected ICollectionService collectionService;
+    @Autowired
     @Qualifier("refEntDao")
     protected IReferencedEntityDao<ReferencedEntityBase> referencedEntityDao;
     @Autowired
+    @Qualifier("sourcedEntityDao")
+    protected ISourcedEntityDao<SourcedEntityBase<?>> sourcedEntityDao;
+    @Autowired
     private INomenclaturalStatusDao nomStatusDao;
     @Autowired
     private ITypeDesignationDao typeDesignationDao;
@@ -110,19 +132,17 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     private ICdmGenericDao genericDao;
     @Autowired
     private ILuceneIndexToolProvider luceneIndexToolProvider;
+    @Autowired
+    // @Qualifier("defaultBeanInitializer")
+    protected IBeanInitializer defaultBeanInitializer;
 
     /**
      * Constructor
      */
-    public NameServiceImpl(){
-        if (logger.isDebugEnabled()) { logger.debug("Load NameService Bean"); }
-    }
+    public NameServiceImpl(){}
 
 //********************* METHODS ****************************************************************//
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.api.service.ServiceBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)
-     */
     @Override
     @Transactional(readOnly = false)
     public DeleteResult delete(UUID nameUUID){
@@ -132,34 +152,30 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public DeleteResult delete(TaxonNameBase name){
+    public DeleteResult delete(TaxonName name){
         return delete(name.getUuid());
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.cdm.api.service.INameService#delete(eu.etaxonomy.cdm.model.name.TaxonNameBase, eu.etaxonomy.cdm.api.service.NameDeletionConfigurator)
-     */
     @Override
     @Transactional(readOnly = false)
-    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
-       DeleteResult result = new DeleteResult();
-               TaxonNameBase name = dao.load(nameUUID);
+    public DeleteResult delete(TaxonName name, NameDeletionConfigurator config) {
+        DeleteResult result = new DeleteResult();
 
-       if (name == null){
-               result.setAbort();
+        if (name == null){
+            result.setAbort();
             return result;
         }
 
-       try{
-               result = this.isDeletable(name.getUuid(), config);
+        try{
+            result = this.isDeletable(name, config);
         }catch(Exception e){
-               result.addException(e);
-               result.setError();
-               return result;
+            result.addException(e);
+            result.setError();
+            return result;
         }
         if (result.isOk()){
         //remove references to this name
-               removeNameRelationshipsByDeleteConfig(name, config);
+            removeNameRelationshipsByDeleteConfig(name, config);
 
            //remove name from homotypical group
             HomotypicalGroup homotypicalGroup = name.getHomotypicalGroup();
@@ -168,31 +184,74 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             }
 
              //all type designation relationships are removed as they belong to the name
-               deleteTypeDesignation(name, null);
-       //              //type designations
-       //              if (! name.getTypeDesignations().isEmpty()){
-       //                      String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
-       //                      throw new ReferrencedObjectUndeletableException(message);
-       //              }
+            deleteTypeDesignation(name, null);
+    //      //type designations
+    //      if (! name.getTypeDesignations().isEmpty()){
+    //          String message = "Name can't be deleted as it has types. Remove types prior to deletion.";
+    //          throw new ReferrencedObjectUndeletableException(message);
+    //      }
+
+            try{
+                dao.delete(name);
+                result.addDeletedObject(name);
+            }catch(Exception e){
+                result.addException(e);
+                result.setError();
+            }
+            return result;
+        }
 
+        return result;
+    }
 
-               try{
-               UUID nameUuid = dao.delete(name);
+    @Override
+    @Transactional(readOnly = false)
+    public DeleteResult delete(UUID nameUUID, NameDeletionConfigurator config) {
 
-               }catch(Exception e){
-                       result.addException(e);
-                       result.setError();
-               }
-               return result;
-        }
+        TaxonName name = dao.load(nameUUID);
+        return delete(name, config);
+    }
 
+    @Override
+    @Transactional(readOnly = false)
+    public UpdateResult cloneTypeDesignation(UUID nameUuid, SpecimenTypeDesignation baseDesignation,
+            String accessionNumber, String barcode, String catalogNumber,
+            UUID collectionUuid, SpecimenTypeDesignationStatus typeStatus){
+        UpdateResult result = new UpdateResult();
+
+        DerivedUnit baseSpecimen = HibernateProxyHelper.deproxy(occurrenceService.load(baseDesignation.getTypeSpecimen().getUuid(), Arrays.asList("collection")), DerivedUnit.class);
+        DerivedUnit duplicate = DerivedUnit.NewInstance(baseSpecimen.getRecordBasis());
+        DerivationEvent derivedFrom = baseSpecimen.getDerivedFrom();
+        Collection<FieldUnit> fieldUnits = occurrenceService.findFieldUnits(baseSpecimen.getUuid(), null);
+        if(fieldUnits.size()!=1){
+            result.addException(new Exception("More than one or no field unit found for specimen"));
+            result.setError();
+            return result;
+        }
+        for (SpecimenOrObservationBase original : derivedFrom.getOriginals()) {
+            DerivationEvent.NewSimpleInstance(original, duplicate, derivedFrom.getType());
+        }
+        duplicate.setAccessionNumber(accessionNumber);
+        duplicate.setBarcode(barcode);
+        duplicate.setCatalogNumber(catalogNumber);
+        duplicate.setCollection(collectionService.load(collectionUuid));
+        SpecimenTypeDesignation typeDesignation = SpecimenTypeDesignation.NewInstance();
+        typeDesignation.setTypeSpecimen(duplicate);
+        typeDesignation.setTypeStatus(typeStatus);
+
+        TaxonName name = load(nameUuid);
+        name.getTypeDesignations().add(typeDesignation);
+
+        result.setCdmEntity(typeDesignation);
+        result.addUpdatedObject(name);
         return result;
     }
 
     @Override
-    public DeleteResult deleteTypeDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation){
-       if(typeDesignation!=null && typeDesignation.getId()!=0){
-               typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
+    @Transactional
+    public DeleteResult deleteTypeDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation){
+       if(typeDesignation != null && typeDesignation .isPersited()){
+               typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignation.getUuid()), TypeDesignationBase.class);
        }
 
         DeleteResult result = new DeleteResult();
@@ -202,27 +261,31 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
         }else if (name != null && typeDesignation != null){
             removeSingleDesignation(name, typeDesignation);
         }else if (name != null){
-            Set<TypeDesignationBase> designationSet = new HashSet<TypeDesignationBase>(name.getTypeDesignations());
-            for (Object o : designationSet){
-                TypeDesignationBase desig = CdmBase.deproxy(o, TypeDesignationBase.class);
+            @SuppressWarnings("rawtypes")
+            Set<TypeDesignationBase<?>> designationSet = new HashSet(name.getTypeDesignations());
+            for (TypeDesignationBase<?> desig : designationSet){
+                desig = CdmBase.deproxy(desig);
                 removeSingleDesignation(name, desig);
             }
         }else if (typeDesignation != null){
-            Set<TaxonNameBase> nameSet = new HashSet<TaxonNameBase>(typeDesignation.getTypifiedNames());
-            for (Object o : nameSet){
-                TaxonNameBase singleName = CdmBase.deproxy(o, TaxonNameBase.class);
+            @SuppressWarnings("unchecked")
+            Set<TaxonName> nameSet = new HashSet(typeDesignation.getTypifiedNames());
+            for (TaxonName singleName : nameSet){
+                singleName = CdmBase.deproxy(singleName);
                 removeSingleDesignation(singleName, typeDesignation);
             }
         }
+        result.addDeletedObject(typeDesignation);
         result.addUpdatedObject(name);
         return result;
     }
 
 
     @Override
+    @Transactional(readOnly = false)
     public DeleteResult deleteTypeDesignation(UUID nameUuid, UUID typeDesignationUuid){
-        TaxonNameBase nameBase = load(nameUuid);
-        TypeDesignationBase typeDesignation = HibernateProxyHelper.deproxy(referencedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
+        TaxonName nameBase = load(nameUuid);
+        TypeDesignationBase<?> typeDesignation = HibernateProxyHelper.deproxy(sourcedEntityDao.load(typeDesignationUuid), TypeDesignationBase.class);
         return deleteTypeDesignation(nameBase, typeDesignation);
     }
 
@@ -230,11 +293,22 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * @param name
      * @param typeDesignation
      */
-    private void removeSingleDesignation(TaxonNameBase name, TypeDesignationBase typeDesignation) {
+    @Transactional
+    private void removeSingleDesignation(TaxonName name, TypeDesignationBase<?> typeDesignation) {
+
         name.removeTypeDesignation(typeDesignation);
         if (typeDesignation.getTypifiedNames().isEmpty()){
             typeDesignation.removeType();
+            if (!typeDesignation.getRegistrations().isEmpty()){
+                for(Object reg: typeDesignation.getRegistrations()){
+                    if (reg instanceof Registration){
+                        ((Registration)reg).removeTypeDesignation(typeDesignation);
+                    }
+                }
+            }
+
             typeDesignationDao.delete(typeDesignation);
+
         }
     }
 
@@ -244,7 +318,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * @param name
      * @param config
      */
-    private void removeNameRelationshipsByDeleteConfig(TaxonNameBase<?,?> name, NameDeletionConfigurator config) {
+    private void removeNameRelationshipsByDeleteConfig(TaxonName name, NameDeletionConfigurator config) {
         try {
             if (config.isRemoveAllNameRelationships()){
                 Set<NameRelationship> rels = getModifiableSet(name.getNameRelations());
@@ -297,12 +371,13 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * new name findByName
      */
     @Override
-    public List<NonViralName> getNamesByNameCache(String nameCache){
-        List result = dao.findByName(nameCache, MatchMode.EXACT, null, null, null, null);
+    @Deprecated
+    public List<TaxonName> getNamesByNameCache(String nameCache){
+        boolean includeAuthors = false;
+        List result = dao.findByName(includeAuthors, nameCache, MatchMode.EXACT, null, null, null, null);
         return result;
     }
 
-
     /**
      * TODO candidate for harmonization
      * new name saveHomotypicalGroups
@@ -310,8 +385,8 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * findByTitle
      */
     @Override
-    public List<NonViralName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
-        List result = dao.findByTitle(titleCache, matchMode, null, null, null ,propertyPaths);
+    public List<TaxonName> findNamesByTitleCache(String titleCache, MatchMode matchMode, List<String> propertyPaths){
+        List result = dao.findByTitle(titleCache, matchMode, null, null, nullpropertyPaths);
         return result;
     }
 
@@ -322,17 +397,47 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * findByTitle
      */
     @Override
-    public List<NonViralName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
-        List result = dao.findByName(nameCache, matchMode, null, null, null ,propertyPaths);
+    public List<TaxonName> findNamesByNameCache(String nameCache, MatchMode matchMode, List<String> propertyPaths){
+        List<TaxonName> result = dao.findByName(false, nameCache, matchMode, null, null, null , propertyPaths);
         return result;
     }
 
+    @Override
+    public Pager<TaxonNameParts> findTaxonNameParts(Optional<String> genusOrUninomial,
+            Optional<String> infraGenericEpithet, Optional<String> specificEpithet,
+            Optional<String> infraSpecificEpithet, Rank rank, Set<UUID> excludedNamesUuids,
+            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
+
+
+        long count = dao.countTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraGenericEpithet, rank, excludedNamesUuids);
+
+        List<TaxonNameParts> results;
+        if(AbstractPagerImpl.hasResultsInRange(count, pageIndex, pageSize)){
+            results = dao.findTaxonNameParts(genusOrUninomial, infraGenericEpithet, specificEpithet, infraSpecificEpithet,
+                    rank, excludedNamesUuids,
+                    pageSize, pageIndex, orderHints);
+        } else {
+            results = new ArrayList<>();
+        }
+
+        return new DefaultPagerImpl<TaxonNameParts>(pageIndex, count, pageSize, results);
+    }
+
     /**
-     * TODO candidate for harmonization
+     * {@inheritDoc}
      */
     @Override
-    public List getNamesByName(String name, CdmBase sessionObject){
-        return super.findCdmObjectsByTitle(name, sessionObject);
+    public Pager<TaxonNameParts> findTaxonNameParts(TaxonNamePartsFilter filter, String namePartQueryString,
+            Integer pageSize, Integer pageIndex, List<OrderHint> orderHints) {
+
+        return findTaxonNameParts(
+                filter.uninomialQueryString(namePartQueryString),
+                filter.infraGenericEpithet(namePartQueryString),
+                filter.specificEpithet(namePartQueryString),
+                filter.infraspecificEpithet(namePartQueryString),
+                filter.getRank(),
+                filter.getExludedNamesUuids(),
+                pageSize, pageIndex, orderHints);
     }
 
     /**
@@ -351,28 +456,10 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      */
     @Override
     @Transactional(readOnly = false)
-    public Map<UUID, TypeDesignationBase> saveTypeDesignationAll(Collection<TypeDesignationBase> typeDesignationCollection){
+    public Map<UUID, TypeDesignationBase<?>> saveTypeDesignationAll(Collection<TypeDesignationBase<?>> typeDesignationCollection){
         return typeDesignationDao.saveAll(typeDesignationCollection);
     }
 
-    /**
-     * TODO candidate for harmonization
-     * new name saveReferencedEntities
-     */
-    @Override
-    @Transactional(readOnly = false)
-    public Map<UUID, ReferencedEntityBase> saveReferencedEntitiesAll(Collection<ReferencedEntityBase> referencedEntityCollection){
-        return referencedEntityDao.saveAll(referencedEntityCollection);
-    }
-
-    /**
-     * TODO candidate for harmonization
-     * new name getNames
-     */
-    public List<TaxonNameBase> getAllNames(int limit, int start){
-        return dao.list(limit, start);
-    }
-
     /**
      * TODO candidate for harmonization
      * new name getNomenclaturalStatus
@@ -382,15 +469,31 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
         return nomStatusDao.list(limit, start);
     }
 
+    @Override
+    public NomenclaturalStatus loadNomenclaturalStatus(UUID uuid,  List<String> propertyPaths){
+        return nomStatusDao.load(uuid, propertyPaths);
+    }
+
     /**
      * TODO candidate for harmonization
      * new name getTypeDesignations
      */
     @Override
-    public List<TypeDesignationBase> getAllTypeDesignations(int limit, int start){
+    public List<TypeDesignationBase<?>> getAllTypeDesignations(int limit, int start){
         return typeDesignationDao.getAllTypeDesignations(limit, start);
     }
-      /**
+
+    @Override
+    public TypeDesignationBase<?> loadTypeDesignation(int id, List<String> propertyPaths){
+        return typeDesignationDao.load(id, propertyPaths);
+    }
+
+    @Override
+    public TypeDesignationBase<?> loadTypeDesignation(UUID uuid, List<String> propertyPaths){
+        return typeDesignationDao.load(uuid, propertyPaths);
+    }
+
+    /**
      * FIXME Candidate for harmonization
      * homotypicalGroupService.list
      */
@@ -418,7 +521,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public Pager<HybridRelationship> getHybridNames(NonViralName name, HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Pager<HybridRelationship> getHybridNames(INonViralName name,        HybridRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         Integer numberOfResults = dao.countHybridNames(name, type);
 
         List<HybridRelationship> results = new ArrayList<HybridRelationship>();
@@ -430,7 +533,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public List<NameRelationship> listNameRelationships(TaxonNameBase name,    Direction direction, NameRelationshipType type, Integer pageSize,
+    public List<NameRelationship> listNameRelationships(TaxonName name,        Direction direction, NameRelationshipType type, Integer pageSize,
             Integer pageNumber, List<OrderHint> orderHints,    List<String> propertyPaths) {
 
         Integer numberOfResults = dao.countNameRelationships(name, direction, type);
@@ -444,7 +547,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
 
 
     protected LuceneSearch prepareFindByFuzzyNameSearch(Class<? extends CdmBase> clazz,
-            NonViralName nvn,
+            INonViralName nvn,
             float accuracy,
             int maxNoOfResults,
             List<Language> languages,
@@ -458,8 +561,8 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
         Builder textQueryBuilder = new Builder();
         textQueryBuilder.setDisableCoord(false);
 
-        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
-        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
 
 //     SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
 //     luceneSearch.setSortFields(sortFields);
@@ -522,8 +625,8 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             List<Language> languages,
             boolean highlightFragments) {
 
-        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
-        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
 
 //     SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
 //     luceneSearch.setSortFields(sortFields);
@@ -553,8 +656,8 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             boolean highlightFragments) {
         Builder textQueryBuilder = new Builder();
 
-        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonNameBase.class);
-        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonNameBase.class);
+        LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, TaxonName.class);
+        QueryFactory queryFactory = luceneIndexToolProvider.newQueryFactoryFor(TaxonName.class);
 
 //     SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
 //     luceneSearch.setSortFields(sortFields);
@@ -579,20 +682,20 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public List<SearchResult<TaxonNameBase>> findByNameFuzzySearch(
+    public List<SearchResult<TaxonName>> findByNameFuzzySearch(
             String name,
             float accuracy,
             List<Language> languages,
             boolean highlightFragments,
             List<String> propertyPaths,
-            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+            int maxNoOfResults) throws IOException, LuceneParseException {
 
         logger.info("Name to fuzzy search for : " + name);
         // parse the input name
         NonViralNameParserImpl parser = new NonViralNameParserImpl();
-        NonViralName nvn = parser.parseFullName(name);
+        INonViralName nvn = parser.parseFullName(name);
         if(name != null && !name.equals("") && nvn == null) {
-            throw new ParseException("Could not parse name " + name);
+            throw new LuceneParseException("Could not parse name " + name);
         }
         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
 
@@ -607,7 +710,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
         ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
 
         @SuppressWarnings("rawtypes")
-        List<SearchResult<TaxonNameBase>> searchResults = searchResultBuilder.createResultSet(
+        List<SearchResult<TaxonName>> searchResults = searchResultBuilder.createResultSet(
                 topDocs, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
 
         return searchResults;
@@ -620,14 +723,14 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             float accuracy,
             List<Language> languages,
             boolean highlightFragments,
-            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+            int maxNoOfResults) throws IOException, LuceneParseException {
 
         logger.info("Name to fuzzy search for : " + name);
         // parse the input name
         NonViralNameParserImpl parser = new NonViralNameParserImpl();
-        NonViralName nvn = parser.parseFullName(name);
+        INonViralName nvn = parser.parseFullName(name);
         if(name != null && !name.equals("") && nvn == null) {
-            throw new ParseException("Could not parse name " + name);
+            throw new LuceneParseException("Could not parse name " + name);
         }
         LuceneSearch luceneSearch = prepareFindByFuzzyNameSearch(null, nvn, accuracy, maxNoOfResults, languages, highlightFragments);
 
@@ -651,7 +754,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             float accuracy,
             List<Language> languages,
             boolean highlightFragments,
-            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+            int maxNoOfResults) throws IOException, LuceneParseException {
 
         logger.info("Name to fuzzy search for : " + name);
 
@@ -676,7 +779,7 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             boolean wildcard,
             List<Language> languages,
             boolean highlightFragments,
-            int maxNoOfResults) throws CorruptIndexException, IOException, ParseException {
+            int maxNoOfResults) throws IOException, LuceneParseException {
 
         logger.info("Name to exact search for : " + name);
 
@@ -699,51 +802,59 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public Pager<NameRelationship> pageNameRelationships(TaxonNameBase name, Direction direction, NameRelationshipType type, Integer pageSize,
+    public Pager<NameRelationship> pageNameRelationships(TaxonName name, Direction direction, NameRelationshipType type, Integer pageSize,
             Integer pageNumber, List<OrderHint> orderHints,    List<String> propertyPaths) {
         List<NameRelationship> results = listNameRelationships(name, direction, type, pageSize, pageNumber, orderHints, propertyPaths);
         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
     }
 
     @Override
-    public List<NameRelationship> listFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public List<NameRelationship> listFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         return listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
     }
 
     @Override
-    public Pager<NameRelationship> pageFromNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Pager<NameRelationship> pageFromNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         List<NameRelationship> results = listNameRelationships(name, Direction.relatedFrom, type, pageSize, pageNumber, orderHints, propertyPaths);
         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
     }
 
     @Override
-    public List<NameRelationship> listToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public List<NameRelationship> listToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         return listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
     }
 
     @Override
-    public Pager<NameRelationship> pageToNameRelationships(TaxonNameBase name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
+    public Pager<NameRelationship> pageToNameRelationships(TaxonName name, NameRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
         List<NameRelationship> results = listNameRelationships(name, Direction.relatedTo, type, pageSize, pageNumber, orderHints, propertyPaths);
         return new DefaultPagerImpl<NameRelationship>(pageNumber, results.size(), pageSize, results);
     }
 
     @Override
-    public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
+    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
             Integer pageSize, Integer pageNumber) {
         return getTypeDesignations(name, status, pageSize, pageNumber, null);
     }
 
     @Override
-    public Pager<TypeDesignationBase> getTypeDesignations(TaxonNameBase name, SpecimenTypeDesignationStatus status,
+    public Pager<TypeDesignationBase> getTypeDesignations(TaxonName name, SpecimenTypeDesignationStatus status,
                 Integer pageSize, Integer pageNumber, List<String> propertyPaths){
-        Integer numberOfResults = dao.countTypeDesignations(name, status);
+        long numberOfResults = dao.countTypeDesignations(name, status);
 
-        List<TypeDesignationBase> results = new ArrayList<TypeDesignationBase>();
-        if(AbstractPagerImpl.hasResultsInRange(numberOfResults.longValue(), pageNumber, pageSize)) {
+        List<TypeDesignationBase> results = new ArrayList<>();
+        if(AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)) {
             results = dao.getTypeDesignations(name, null, status, pageSize, pageNumber, propertyPaths);
         }
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
+    }
 
-        return new DefaultPagerImpl<TypeDesignationBase>(pageNumber, numberOfResults, pageSize, results);
+    @Override
+    public List<TypeDesignationBase> getTypeDesignationsInHomotypicalGroup(UUID nameUuid, Integer pageSize,
+            Integer pageNumber, List<String> propertyPaths){
+        TaxonName name = load(nameUuid, Arrays.asList("nomenclaturalReference.authorship"));
+        Set<TypeDesignationBase<?>> typeDesignations = name.getHomotypicalGroup().getTypeDesignations();
+        List<TypeDesignationBase> result = defaultBeanInitializer.initializeAll(new ArrayList(typeDesignations), propertyPaths);
+        return result;
     }
 
     /**
@@ -751,16 +862,16 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
      * rename search
      */
     @Override
-    public Pager<TaxonNameBase> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize,     Integer pageNumber, List<OrderHint> orderHints,
+    public Pager<TaxonName> searchNames(String uninomial,String infraGenericEpithet, String specificEpithet, String infraspecificEpithet, Rank rank, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
             List<String> propertyPaths) {
-        Integer numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
+        long numberOfResults = dao.countNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank);
 
-        List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
+        List<TaxonName> results = new ArrayList<>();
         if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
             results = dao.searchNames(uninomial, infraGenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber, orderHints, propertyPaths);
         }
 
-        return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
+        return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @Override
@@ -769,15 +880,16 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
     }
 
     @Override
-    public Pager<TaxonNameBase> findByName(Class<? extends TaxonNameBase> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria, Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
-        Integer numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
+    public Pager<TaxonName> findByName(Class<TaxonName> clazz, String queryString, MatchMode matchmode, List<Criterion> criteria,
+            Integer pageSize,Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
+         Long numberOfResults = dao.countByName(clazz, queryString, matchmode, criteria);
 
-         List<TaxonNameBase> results = new ArrayList<TaxonNameBase>();
+         List<TaxonName> results = new ArrayList<>();
          if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
                 results = dao.findByName(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
          }
 
-          return new DefaultPagerImpl<TaxonNameBase>(pageNumber, numberOfResults, pageSize, results);
+         return new DefaultPagerImpl<>(pageNumber, numberOfResults, pageSize, results);
     }
 
     @Override
@@ -787,88 +899,72 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
 
     @Override
     @Transactional(readOnly = false)
-    public void updateTitleCache(Class<? extends TaxonNameBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonNameBase> cacheStrategy, IProgressMonitor monitor) {
+    public UpdateResult updateCaches(Class<? extends TaxonName> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonName> cacheStrategy, IProgressMonitor monitor) {
         if (clazz == null){
-            clazz = TaxonNameBase.class;
+            clazz = TaxonName.class;
         }
-        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
+        return super.updateCachesImpl(clazz, stepSize, cacheStrategy, monitor);
     }
 
 
-    @Override
-    protected void setOtherCachesNull(TaxonNameBase name) {
-        if (name.isInstanceOf(NonViralName.class)){
-            NonViralName<?> nvn = CdmBase.deproxy(name, NonViralName.class);
-            if (! nvn.isProtectedNameCache()){
-                nvn.setNameCache(null, false);
-            }
-            if (! nvn.isProtectedAuthorshipCache()){
-                nvn.setAuthorshipCache(null, false);
-            }
-            if (! nvn.isProtectedFullTitleCache()){
-                nvn.setFullTitleCache(null, false);
-            }
-        }
-    }
-
     @Override
     public List<TaggedText> getTaggedName(UUID uuid) {
-        TaxonNameBase<?,?> taxonNameBase = dao.load(uuid);
-        List<TaggedText> taggedName = taxonNameBase.getTaggedName();
+        TaxonName taxonName = dao.load(uuid);
+        List<TaggedText> taggedName = taxonName.getTaggedName();
         return taggedName;
     }
 
-    @Override
-    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
-       DeleteResult result = new DeleteResult();
-       TaxonNameBase name = this.load(nameUUID);
-       NameDeletionConfigurator nameConfig = null;
-       if (config instanceof NameDeletionConfigurator){
-               nameConfig = (NameDeletionConfigurator) config;
-       }else{
-                result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
-                result.setError();
-                return result;
-       }
 
-       if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
-               HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
+    public DeleteResult isDeletable(TaxonName name, DeleteConfiguratorBase config){
+        DeleteResult result = new DeleteResult();
+
+        NameDeletionConfigurator nameConfig = null;
+        if (config instanceof NameDeletionConfigurator){
+            nameConfig = (NameDeletionConfigurator) config;
+        }else{
+             result.addException(new Exception("The delete configurator should be of the type NameDeletionConfigurator."));
+             result.setError();
+             return result;
+        }
+
+        if (!name.getNameRelations().isEmpty() && !nameConfig.isRemoveAllNameRelationships()){
+            HomotypicalGroup homotypicalGroup = HibernateProxyHelper.deproxy(name.getHomotypicalGroup(), HomotypicalGroup.class);
 
-               if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
-                               result.addException(new Exception( "Name can't be deleted as it is a basionym."));
-                               result.setAbort();
+            if (!nameConfig.isIgnoreIsBasionymFor() && homotypicalGroup.getBasionyms().contains(name)){
+                result.addException(new Exception( "Name can't be deleted as it is a basionym."));
+                result.setAbort();
             }
             if (!nameConfig.isIgnoreHasBasionym() && (name.getBasionyms().size()>0)){
-               result.addException(new Exception( "Name can't be deleted as it has a basionym."));
-               result.setAbort();
+                result.addException(new Exception( "Name can't be deleted as it has a basionym."));
+                result.setAbort();
             }
             Set<NameRelationship> relationships = name.getNameRelations();
             for (NameRelationship rel: relationships){
-               if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
-                       result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
-                       result.setAbort();
-                       break;
-               }
+                if (!rel.getType().equals(NameRelationshipType.BASIONYM())){
+                    result.addException(new Exception("Name can't be deleted as it is used in name relationship(s). Remove name relationships prior to deletion."));
+                    result.setAbort();
+                    break;
+                }
             }
         }
 
         //concepts
         if (! name.getTaxonBases().isEmpty()){
-               result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
-               result.setAbort();
+            result.addException(new Exception("Name can't be deleted as it is used in concept(s). Remove or change concept prior to deletion."));
+            result.setAbort();
         }
 
         //hybrid relationships
-        if (name.isInstanceOf(NonViralName.class)){
-            NonViralName nvn = CdmBase.deproxy(name, NonViralName.class);
+        if (name.isNonViral()){
+            INonViralName nvn = name;
             Set<HybridRelationship> parentHybridRelations = nvn.getHybridParentRelations();
             //Hibernate.initialize(parentHybridRelations);
             if (! parentHybridRelations.isEmpty()){
-               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."));
-               result.setAbort();
+                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."));
+                result.setAbort();
             }
         }
-       Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
+        Set<CdmBase> referencingObjects = genericDao.getReferencingObjectsForDeletion(name);
         for (CdmBase referencingObject : referencingObjects){
             //DerivedUnit?.storedUnder
             if (referencingObject.isInstanceOf(DerivedUnit.class)){
@@ -918,14 +1014,21 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
             result.addException(new Exception(message));
             result.setAbort();
         }
-       return result;
+        return result;
 
     }
 
+
+    @Override
+    public DeleteResult isDeletable(UUID nameUUID, DeleteConfiguratorBase config){
+        TaxonName name = this.load(nameUUID);
+        return isDeletable(name, config);
+    }
+
     @Override
     @Transactional(readOnly = true)
     public UpdateResult setAsGroupsBasionym(UUID nameUuid) {
-        TaxonNameBase name = dao.load(nameUuid);
+        TaxonName name = dao.load(nameUuid);
         UpdateResult result = new UpdateResult();
         name.setAsGroupsBasionym();
         result.addUpdatedObject(name);
@@ -935,9 +1038,52 @@ public class NameServiceImpl extends IdentifiableServiceBase<TaxonNameBase,ITaxo
 
     @Override
     public List<HashMap<String,String>> getNameRecords(){
-
                return dao.getNameRecords();
 
     }
 
+    @Override
+    public List<TypeDesignationStatusBase> getTypeDesignationStatusInUse(){
+        return typeDesignationDao.getTypeDesignationStatusInUse();
+    }
+
+    @Override
+    public Collection<TypeDesignationStatusFilter> getTypeDesignationStatusFilterTerms(List<Language> preferredLanguages){
+        List<TypeDesignationStatusBase> termList = typeDesignationDao.getTypeDesignationStatusInUse();
+        Map<String, TypeDesignationStatusFilter>  filterMap = new HashMap<>();
+        for(TypeDesignationStatusBase term : termList){
+            TypeDesignationStatusFilter filter = new TypeDesignationStatusFilter(term, preferredLanguages, true);
+            String key = filter.getKey();
+            if(filterMap.containsKey(key)){
+                filterMap.get(key).addStatus(term);
+            } else {
+                filterMap.put(key, filter);
+            }
+        }
+        return filterMap.values();
+    }
+
+    @Override
+    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
+        return page(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
+    }
+
+    @Override
+    public <S extends TaxonName> Pager<S> page(Class<S> clazz, List<Restriction<?>> restrictions, Integer pageSize,
+            Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths, boolean includeUnpublished) {
+
+        List<S> records;
+        long resultSize = dao.count(clazz, restrictions);
+        if(AbstractPagerImpl.hasResultsInRange(resultSize, pageIndex, pageSize)){
+            records = dao.list(clazz, restrictions, pageSize, pageIndex, orderHints, propertyPaths, includeUnpublished);
+        } else {
+            records = new ArrayList<>();
+        }
+        Pager<S> pager = new DefaultPagerImpl<>(pageIndex, resultSize, pageSize, records);
+        return pager;
+    }
+
+
+
 }