fixing #4112 (portal/taxon/{uuid}/accepted/{uuid} takes exceedingly much time in...
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / description / DescriptionDaoImpl.java
index 2d8c4e44ee40fc7431d424496c8bbb10425e4247..7b5029ee749f4a43f3d3c7424a141fb8a51f40f6 100644 (file)
 /**\r
 * Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
+* European Distributed Institute of Taxonomy\r
 * http://www.e-taxonomy.eu\r
-* \r
+*\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
 */\r
 \r
 package eu.etaxonomy.cdm.persistence.dao.hibernate.description;\r
 \r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
 import java.util.List;\r
+import java.util.Set;\r
+import java.util.UUID;\r
 \r
 import org.apache.log4j.Logger;\r
 import org.hibernate.Criteria;\r
+import org.hibernate.Query;\r
+import org.hibernate.criterion.ProjectionList;\r
+import org.hibernate.criterion.Projections;\r
+import org.hibernate.criterion.Restrictions;\r
+import org.hibernate.envers.query.AuditEntity;\r
+import org.hibernate.envers.query.AuditQuery;\r
+import org.springframework.beans.factory.annotation.Qualifier;\r
 import org.springframework.stereotype.Repository;\r
 \r
+import eu.etaxonomy.cdm.model.common.DefinedTerm;\r
+import eu.etaxonomy.cdm.model.common.LSID;\r
+import eu.etaxonomy.cdm.model.common.MarkerType;\r
+import eu.etaxonomy.cdm.model.description.CommonTaxonName;\r
 import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
 import eu.etaxonomy.cdm.model.description.Feature;\r
-import eu.etaxonomy.cdm.model.description.FeatureTree;\r
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;\r
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
+import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.description.TaxonNameDescription;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\r
+import eu.etaxonomy.cdm.model.media.Media;\r
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
+import eu.etaxonomy.cdm.model.taxon.Taxon;\r
+import eu.etaxonomy.cdm.model.view.AuditEvent;\r
+import eu.etaxonomy.cdm.persistence.dao.common.OperationNotSupportedInPriorViewException;\r
 import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;\r
 import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;\r
-\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
 \r
 @Repository\r
+@Qualifier("descriptionDaoImpl")\r
 public class DescriptionDaoImpl extends IdentifiableDaoBase<DescriptionBase> implements IDescriptionDao{\r
-       private static final Logger logger = Logger.getLogger(DescriptionDaoImpl.class);\r
 \r
-       public DescriptionDaoImpl() {\r
-               super(DescriptionBase.class); \r
-       }\r
-       \r
+    private static final Logger logger = Logger.getLogger(DescriptionDaoImpl.class);\r
+\r
+    public DescriptionDaoImpl() {\r
+        super(DescriptionBase.class);\r
+        indexedClasses = new Class[3];\r
+        indexedClasses[0] = TaxonDescription.class;\r
+        indexedClasses[1] = TaxonNameDescription.class;\r
+        indexedClasses[2] = SpecimenDescription.class;\r
+    }\r
+\r
+//    @Override  //Override for testing\r
+//    public DescriptionBase load(UUID uuid, List<String> propertyPaths){\r
+//     DescriptionBase bean = findByUuid(uuid);\r
+//        if(bean == null){\r
+//            return bean;\r
+//        }\r
+//        defaultBeanInitializer.initialize(bean, propertyPaths);\r
+//\r
+//        return bean;\r
+//    }\r
+\r
+    @Override\r
+    public int countDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status) {\r
+        checkNotInPriorView("DescriptionDaoImpl.countDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status)");\r
+        Query query = null;\r
+\r
+        if(status == null) {\r
+            query = getSession().createQuery("select count(distinct description) from TaxonDescription description left join description.descriptionElements element join element.area area where area in (:namedAreas)");\r
+        } else {\r
+            query = getSession().createQuery("select count(distinct description) from TaxonDescription description left join description.descriptionElements element join element.area area  join element.status status where area in (:namedAreas) and status = :status");\r
+            query.setParameter("status", status);\r
+        }\r
+        query.setParameterList("namedAreas", namedAreas);\r
+\r
+        return ((Long)query.uniqueResult()).intValue();\r
+    }\r
+\r
+    @Override\r
+    public int countDescriptionElements(DescriptionBase description, Set<Feature> features, Class<? extends DescriptionElementBase> clazz) {\r
+        return countDescriptionElements(description, null, features, clazz);\r
+    }\r
+\r
+    @Override\r
+    public int countDescriptionElements(DescriptionBase description, Class<? extends DescriptionBase> descriptionType,\r
+            Set<Feature> features, Class<? extends DescriptionElementBase> clazz) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = null;\r
+            if(clazz == null) {\r
+                criteria = getSession().createCriteria(DescriptionElementBase.class);\r
+            } else {\r
+                criteria = getSession().createCriteria(clazz);\r
+            }\r
+\r
+            if(description != null) {\r
+                criteria.add(Restrictions.eq("inDescription", description));\r
+            }\r
+\r
+            if(descriptionType != null) {\r
+                criteria.createAlias("inDescription", "d").add(Restrictions.eq("d.class", descriptionType));\r
+            }\r
+\r
+            if(features != null && !features.isEmpty()) {\r
+                criteria.add(Restrictions.in("feature", features));\r
+            }\r
+\r
+            criteria.setProjection(Projections.rowCount());\r
+\r
+            return ((Number)criteria.uniqueResult()).intValue();\r
+        } else {\r
+            if(features != null && !features.isEmpty()) {\r
+                Integer count = 0;\r
+                for(Feature f : features) {\r
+                    AuditQuery query = null;\r
+                    if(clazz == null) {\r
+                        query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());\r
+                    } else {\r
+                        query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());\r
+                    }\r
+\r
+                    if(description != null) {\r
+                        query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));\r
+                    }\r
+\r
+                    if(descriptionType != null) {\r
+                        query.add(AuditEntity.property("inDescription.class").eq(descriptionType));\r
+                    }\r
+\r
+                    query.add(AuditEntity.relatedId("feature").eq(f.getId()));\r
+                    query.addProjection(AuditEntity.id().count("id"));\r
+                    count += ((Long)query.getSingleResult()).intValue();\r
+                }\r
+\r
+                return count;\r
+            } else {\r
+                AuditQuery query = null;\r
+                if(clazz == null) {\r
+                    query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());\r
+                } else {\r
+                    query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());\r
+                }\r
+\r
+                if(description != null) {\r
+                    query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));\r
+                }\r
+                if(descriptionType != null) {\r
+                    query.add(AuditEntity.property("inDescription.class").eq(descriptionType));\r
+                }\r
+\r
+                query.addProjection(AuditEntity.id().count("id"));\r
+                return ((Long)query.getSingleResult()).intValue();\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public int countDescriptions(Class<? extends DescriptionBase> clazz, Boolean hasImages, Boolean hasText, Set<Feature> features) {\r
+        checkNotInPriorView("DescriptionDaoImpl.countDescriptions(Class<TYPE> type, Boolean hasImages, Boolean hasText, Set<Feature> features)");\r
+        Criteria inner = null;\r
+\r
+        if(clazz == null) {\r
+            inner = getSession().createCriteria(type);\r
+        } else {\r
+            inner = getSession().createCriteria(clazz);\r
+        }\r
+\r
+        Criteria elementsCriteria = inner.createCriteria("descriptionElements");\r
+        if(hasText != null) {\r
+            if(hasText) {\r
+                elementsCriteria.add(Restrictions.isNotEmpty("multilanguageText"));\r
+            } else {\r
+                elementsCriteria.add(Restrictions.isEmpty("multilanguageText"));\r
+            }\r
+        }\r
+\r
+        if(hasImages != null) {\r
+            if(hasImages) {\r
+                elementsCriteria.add(Restrictions.isNotEmpty("media"));\r
+            } else {\r
+                elementsCriteria.add(Restrictions.isEmpty("media"));\r
+            }\r
+        }\r
+\r
+        if(features != null && !features.isEmpty()) {\r
+            elementsCriteria.add(Restrictions.in("feature", features));\r
+        }\r
+\r
+        inner.setProjection(Projections.countDistinct("id"));\r
+\r
+        return ((Number) inner.uniqueResult()).intValue();\r
+    }\r
+\r
+    @Override\r
+    public int countTaxonDescriptions(Taxon taxon, Set<DefinedTerm> scopes,Set<NamedArea> geographicalScopes, Set<MarkerType> markerTypes) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = getSession().createCriteria(TaxonDescription.class);\r
+\r
+            if(taxon != null) {\r
+                criteria.add(Restrictions.eq("taxon", taxon));\r
+            }\r
+\r
+            if(scopes != null && !scopes.isEmpty()) {\r
+                Set<Integer> scopeIds = new HashSet<Integer>();\r
+                for(DefinedTerm s : scopes) {\r
+                    scopeIds.add(s.getId());\r
+                }\r
+                criteria.createCriteria("scopes").add(Restrictions.in("id", scopeIds));\r
+            }\r
+\r
+            if(geographicalScopes != null && !geographicalScopes.isEmpty()) {\r
+                Set<Integer> geoScopeIds = new HashSet<Integer>();\r
+                for(NamedArea n : geographicalScopes) {\r
+                    geoScopeIds.add(n.getId());\r
+                }\r
+                criteria.createCriteria("geoScopes").add(Restrictions.in("id", geoScopeIds));\r
+            }\r
+\r
+\r
+            addMarkerTypesCriterion(markerTypes, criteria);\r
+\r
+\r
+            criteria.setProjection(Projections.rowCount());\r
+\r
+            return ((Number)criteria.uniqueResult()).intValue();\r
+        } else {\r
+            if((scopes == null || scopes.isEmpty())&& (geographicalScopes == null || geographicalScopes.isEmpty()) && (markerTypes == null || markerTypes.isEmpty())) {\r
+                AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonDescription.class,auditEvent.getRevisionNumber());\r
+                if(taxon != null) {\r
+                    query.add(AuditEntity.relatedId("taxon").eq(taxon.getId()));\r
+                }\r
+\r
+                query.addProjection(AuditEntity.id().count("id"));\r
+\r
+                return ((Long)query.getSingleResult()).intValue();\r
+            } else {\r
+                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptions(Taxon taxon, Set<Scope> scopes,Set<NamedArea> geographicalScopes)");\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param markerTypes\r
+     * @param criteria\r
+     *\r
+     */\r
+    //TODO move to AnnotatableEntityDao(?)\r
+    private void addMarkerTypesCriterion(Set<MarkerType> markerTypes, Criteria criteria) {\r
+\r
+        if(markerTypes != null && !markerTypes.isEmpty()) {\r
+            Set<Integer> markerTypeIds = new HashSet<Integer>();\r
+            for(MarkerType markerType : markerTypes) {\r
+                markerTypeIds.add(markerType.getId());\r
+            }\r
+            criteria.createCriteria("markers").add(Restrictions.eq("flag", true))\r
+                    .createAlias("markerType", "mt")\r
+                     .add(Restrictions.in("mt.id", markerTypeIds));\r
+        } else if (markerTypes != null && markerTypes.isEmpty()){\r
+            //AT: added in case the projects requires an third state description, An empty Marker type set\r
+        }\r
+    }\r
+    @Override\r
+    public List<DescriptionElementBase> getDescriptionElements(\r
+            DescriptionBase description, Set<Feature> features,\r
+            Class<? extends DescriptionElementBase> clazz, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+        return getDescriptionElements(description, null, features, clazz, pageSize, pageNumber, propertyPaths);\r
+    }\r
+\r
+    @Override\r
+    public List<DescriptionElementBase> getDescriptionElements(\r
+            DescriptionBase description, Class<? extends DescriptionBase> descriptionType,\r
+            Set<Feature> features,\r
+            Class<? extends DescriptionElementBase> clazz,\r
+            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = null;\r
+            if(clazz == null) {\r
+                criteria = getSession().createCriteria(DescriptionElementBase.class);\r
+            } else {\r
+                criteria = getSession().createCriteria(clazz);\r
+            }\r
+\r
+            if(description != null) {\r
+                criteria.add(Restrictions.eq("inDescription", description));\r
+            }\r
+            if(descriptionType != null) {\r
+                criteria.createAlias("inDescription", "d").add(Restrictions.eq("d.class", descriptionType));\r
+            }\r
+\r
+            if(features != null && !features.isEmpty()) {\r
+                criteria.add(Restrictions.in("feature", features));\r
+            }\r
+\r
+            if(pageSize != null) {\r
+                criteria.setMaxResults(pageSize);\r
+                if(pageNumber != null) {\r
+                    criteria.setFirstResult(pageNumber * pageSize);\r
+                }\r
+            }\r
+\r
+            List<DescriptionElementBase> results = criteria.list();\r
+\r
+            defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+            return results;\r
+        } else {\r
+            List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();\r
+            if(features != null && !features.isEmpty()) {\r
+\r
+                for(Feature f : features) {\r
+                    AuditQuery query = null;\r
+                    if(clazz == null) {\r
+                        query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());\r
+                    } else {\r
+                        query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());\r
+                    }\r
+\r
+                    if(description != null) {\r
+                        query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));\r
+                    }\r
+\r
+                    if(descriptionType != null) {\r
+                        query.add(AuditEntity.property("inDescription.class").eq(descriptionType));\r
+                    }\r
+\r
+                    query.add(AuditEntity.relatedId("feature").eq(f.getId()));\r
+                    result.addAll(query.getResultList());\r
+                }\r
+            } else {\r
+                AuditQuery query = null;\r
+                if(clazz == null) {\r
+                    query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());\r
+                } else {\r
+                    query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());\r
+                }\r
+\r
+                if(description != null) {\r
+                    query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));\r
+                }\r
+\r
+                if(descriptionType != null) {\r
+                    query.add(AuditEntity.property("inDescription.class").eq(descriptionType));\r
+                }\r
+\r
+                result = query.getResultList();\r
+            }\r
+\r
+            defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+\r
+            return result;\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public List<TaxonDescription> listTaxonDescriptions(Taxon taxon, Set<DefinedTerm> scopes, Set<NamedArea> geographicalScopes, Set<MarkerType> markerTypes, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = getSession().createCriteria(TaxonDescription.class);\r
+\r
+            if(taxon != null) {\r
+                criteria.add(Restrictions.eq("taxon", taxon));\r
+            }\r
+\r
+            if(scopes != null && !scopes.isEmpty()) {\r
+                Set<Integer> scopeIds = new HashSet<Integer>();\r
+                for(DefinedTerm s : scopes) {\r
+                    scopeIds.add(s.getId());\r
+                }\r
+                criteria.createCriteria("scopes").add(Restrictions.in("id", scopeIds));\r
+            }\r
+\r
+            if(geographicalScopes != null && !geographicalScopes.isEmpty()) {\r
+                Set<Integer> geoScopeIds = new HashSet<Integer>();\r
+                for(NamedArea n : geographicalScopes) {\r
+                    geoScopeIds.add(n.getId());\r
+                }\r
+                criteria.createCriteria("geoScopes").add(Restrictions.in("id", geoScopeIds));\r
+            }\r
+\r
+            addMarkerTypesCriterion(markerTypes, criteria);\r
+\r
+            if(pageSize != null) {\r
+                criteria.setMaxResults(pageSize);\r
+                if(pageNumber != null) {\r
+                    criteria.setFirstResult(pageNumber * pageSize);\r
+                }\r
+            }\r
+\r
+            List<TaxonDescription> results = criteria.list();\r
+\r
+            defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+            return results;\r
+        } else {\r
+            if((scopes == null || scopes.isEmpty())&& (geographicalScopes == null || geographicalScopes.isEmpty())&& (markerTypes == null || markerTypes.isEmpty())) {\r
+                AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonDescription.class,auditEvent.getRevisionNumber());\r
+                if(taxon != null) {\r
+                    query.add(AuditEntity.relatedId("taxon").eq(taxon.getId()));\r
+                }\r
+\r
+                if(pageSize != null) {\r
+                    query.setMaxResults(pageSize);\r
+                    if(pageNumber != null) {\r
+                        query.setFirstResult(pageNumber * pageSize);\r
+                    } else {\r
+                        query.setFirstResult(0);\r
+                    }\r
+                }\r
+\r
+                List<TaxonDescription> results = query.getResultList();\r
+                defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+                return results;\r
+            } else {\r
+                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptions(Taxon taxon, Set<Scope> scopes,Set<NamedArea> geographicalScopes)");\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public List<TaxonNameDescription> getTaxonNameDescriptions(TaxonNameBase name, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = getSession().createCriteria(TaxonNameDescription.class);\r
+\r
+          if(name != null) {\r
+              criteria.add(Restrictions.eq("taxonName", name));\r
+          }\r
+\r
+          if(pageSize != null) {\r
+              criteria.setMaxResults(pageSize);\r
+              if(pageNumber != null) {\r
+                  criteria.setFirstResult(pageNumber * pageSize);\r
+              }\r
+          }\r
+\r
+          List<TaxonNameDescription> results = criteria.list();\r
+\r
+          defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+          return results;\r
+        } else {\r
+            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonNameDescription.class,auditEvent.getRevisionNumber());\r
+\r
+            if(name != null) {\r
+                query.add(AuditEntity.relatedId("taxonName").eq(name.getId()));\r
+            }\r
+\r
+            if(pageSize != null) {\r
+                  query.setMaxResults(pageSize);\r
+                  if(pageNumber != null) {\r
+                      query.setFirstResult(pageNumber * pageSize);\r
+                  }\r
+            }\r
+\r
+            List<TaxonNameDescription> results = query.getResultList();\r
+\r
+            defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+            return results;\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public int countTaxonNameDescriptions(TaxonNameBase name) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = getSession().createCriteria(TaxonNameDescription.class);\r
+\r
+            if(name != null) {\r
+                criteria.add(Restrictions.eq("taxonName", name));\r
+            }\r
+\r
+            criteria.setProjection(Projections.rowCount());\r
+\r
+            return ((Number)criteria.uniqueResult()).intValue();\r
+        } else {\r
+            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonNameDescription.class,auditEvent.getRevisionNumber());\r
+\r
+            if(name != null) {\r
+                query.add(AuditEntity.relatedId("taxonName").eq(name.getId()));\r
+            }\r
+\r
+            query.addProjection(AuditEntity.id().count("id"));\r
+            return ((Long)query.getSingleResult()).intValue();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Should use a DetachedCriteria & subquery, but HHH-158 prevents this, for now.\r
+     *\r
+     * e.g. DetachedCriteria inner = DestachedCriteria.forClass(type);\r
+     *\r
+     * outer.add(Subqueries.propertyIn("id", inner));\r
+     */\r
+    @Override\r
+    public List<DescriptionBase> listDescriptions(Class<? extends DescriptionBase> clazz, Boolean hasImages, Boolean hasText,  Set<Feature> features, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        checkNotInPriorView("DescriptionDaoImpl.listDescriptions(Class<TYPE> type, Boolean hasImages, Boolean hasText, Set<Feature> features, Integer pageSize, Integer pageNumber)");\r
+        Criteria inner = null;\r
+\r
+        if(clazz == null) {\r
+            inner = getSession().createCriteria(type);\r
+        } else {\r
+            inner = getSession().createCriteria(clazz);\r
+        }\r
+\r
+        Criteria elementsCriteria = inner.createCriteria("descriptionElements");\r
+        if(hasText != null) {\r
+            if(hasText) {\r
+                elementsCriteria.add(Restrictions.isNotEmpty("multilanguageText"));\r
+            } else {\r
+                elementsCriteria.add(Restrictions.isEmpty("multilanguageText"));\r
+            }\r
+        }\r
+\r
+        if(hasImages != null) {\r
+            if(hasImages) {\r
+                elementsCriteria.add(Restrictions.isNotEmpty("media"));\r
+            } else {\r
+                elementsCriteria.add(Restrictions.isEmpty("media"));\r
+            }\r
+        }\r
+\r
+        if(features != null && !features.isEmpty()) {\r
+            elementsCriteria.add(Restrictions.in("feature", features));\r
+        }\r
+\r
+        inner.setProjection(Projections.distinct(Projections.id()));\r
+\r
+        List<Object> intermediateResult = inner.list();\r
+\r
+        if(intermediateResult.isEmpty()) {\r
+            return new ArrayList<DescriptionBase>();\r
+        }\r
+\r
+        Integer[] resultIds = new Integer[intermediateResult.size()];\r
+        for(int i = 0; i < resultIds.length; i++) {\r
+                resultIds[i] = ((Number)intermediateResult.get(i)).intValue();\r
+        }\r
+\r
+        Criteria outer = null;\r
+\r
+        if(clazz == null) {\r
+            outer = getSession().createCriteria(type);\r
+        } else {\r
+            outer = getSession().createCriteria(clazz);\r
+        }\r
+\r
+        outer.add(Restrictions.in("id", resultIds));\r
+        addOrder(outer, orderHints);\r
+\r
+        if(pageSize != null) {\r
+            outer.setMaxResults(pageSize);\r
+            if(pageNumber != null) {\r
+                outer.setFirstResult(pageNumber * pageSize);\r
+            }\r
+        }\r
+\r
+        List<DescriptionBase> results = outer.list();\r
+        defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+        return results;\r
+    }\r
+\r
+    @Override\r
+    public List<TaxonDescription> searchDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        checkNotInPriorView("DescriptionDaoImpl.searchDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");\r
+\r
+        Criteria criteria = getSession().createCriteria(TaxonDescription.class);\r
+        Criteria elements = criteria.createCriteria("descriptionElements", "descriptionElement", Criteria.LEFT_JOIN);\r
+        elements.add(Restrictions.in("area", namedAreas.toArray()));\r
+\r
+        if(status != null) {\r
+            elements.add(Restrictions.eq("status", status));\r
+        }\r
+\r
+        ProjectionList projectionList = Projections.projectionList().add(Projections.id());\r
+\r
+        if(orderHints != null && !orderHints.isEmpty()) {\r
+            for(OrderHint orderHint : orderHints) {\r
+                projectionList = projectionList.add(Projections.property(orderHint.getPropertyName()));\r
+            }\r
+        }\r
+\r
+        criteria.setProjection(Projections.distinct(projectionList));\r
+\r
+        if(pageSize != null) {\r
+            criteria.setMaxResults(pageSize);\r
+            if(pageNumber != null) {\r
+                criteria.setFirstResult(pageNumber * pageSize);\r
+            }\r
+        }\r
+\r
+        addOrder(criteria,orderHints);\r
+\r
+        List<Object> intermediateResult = criteria.list();\r
+\r
+        if(intermediateResult.isEmpty()) {\r
+            return new ArrayList<TaxonDescription>();\r
+        }\r
+\r
+        Integer[] resultIds = new Integer[intermediateResult.size()];\r
+        for(int i = 0; i < resultIds.length; i++) {\r
+            if(orderHints == null || orderHints.isEmpty()) {\r
+                resultIds[i] = ((Number)intermediateResult.get(i)).intValue();\r
+            } else {\r
+              resultIds[i] = ((Number)((Object[])intermediateResult.get(i))[0]).intValue();\r
+            }\r
+        }\r
+\r
+        criteria = getSession().createCriteria(TaxonDescription.class);\r
+        criteria.add(Restrictions.in("id", resultIds));\r
+        addOrder(criteria,orderHints);\r
+\r
+        List<TaxonDescription> results = criteria.list();\r
+        defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+        return results;\r
+    }\r
+\r
+    @Override\r
+    public List<CommonTaxonName> searchDescriptionByCommonName(String queryString, MatchMode matchMode, Integer pageSize, Integer pageNumber) {\r
+\r
+        Criteria crit = getSession().createCriteria(CommonTaxonName.class);\r
+        if (matchMode == MatchMode.EXACT) {\r
+            crit.add(Restrictions.eq("name", matchMode.queryStringFrom(queryString)));\r
+        } else {\r
+            crit.add(Restrictions.ilike("name", matchMode.queryStringFrom(queryString)));\r
+        }\r
+\r
+        if(pageSize != null) {\r
+            crit.setMaxResults(pageSize);\r
+            if(pageNumber != null) {\r
+                crit.setFirstResult(pageNumber * pageSize);\r
+            }\r
+        }\r
+        List<CommonTaxonName> results = crit.list();\r
+        return results;\r
+    }\r
+\r
+    @Override\r
+    public Integer countDescriptionByCommonName(String queryString, MatchMode matchMode) {\r
+        //TODO inprove performance\r
+        List<CommonTaxonName> results =  searchDescriptionByCommonName(queryString, matchMode, null, null);\r
+        return results.size();\r
+    }\r
+\r
+    @Override\r
+    public DescriptionBase find(LSID lsid) {\r
+        DescriptionBase descriptionBase = super.find(lsid);\r
+        if(descriptionBase != null) {\r
+            List<String> propertyPaths = new ArrayList<String>();\r
+            propertyPaths.add("createdBy");\r
+            propertyPaths.add("updatedBy");\r
+            propertyPaths.add("taxon");\r
+            propertyPaths.add("taxonName");\r
+            propertyPaths.add("descriptionElements");\r
+            propertyPaths.add("descriptionElements.createdBy");\r
+            propertyPaths.add("descriptionElements.updatedBy");\r
+            propertyPaths.add("descriptionElements.feature");\r
+            propertyPaths.add("descriptionElements.multilanguageText");\r
+            propertyPaths.add("descriptionElements.multilanguageText.language");\r
+            propertyPaths.add("descriptionElements.area");\r
+            propertyPaths.add("descriptionElements.status");\r
+            propertyPaths.add("descriptionElements.modifyingText");\r
+            propertyPaths.add("descriptionElementsmodifyingText.language");\r
+            propertyPaths.add("descriptionElements.modifiers");\r
+\r
+            defaultBeanInitializer.initialize(descriptionBase, propertyPaths);\r
+        }\r
+        return descriptionBase;\r
+    }\r
+\r
+\r
+    @Override\r
+    public <T extends DescriptionElementBase> List<T> getDescriptionElementForTaxon(\r
+            UUID taxonUuid, Set<Feature> features,\r
+            Class<T> type, Integer pageSize,\r
+            Integer pageNumber, List<String> propertyPaths) {\r
+\r
+        Query query = prepareGetDescriptionElementForTaxon(taxonUuid, features, type, pageSize, pageNumber, false);\r
+\r
+        if (logger.isDebugEnabled()){logger.debug(" dao: get list ...");}\r
+        List<T> results = query.list();\r
+        if (logger.isDebugEnabled()){logger.debug(" dao: initialize ...");}\r
+        defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+        if (logger.isDebugEnabled()){logger.debug(" dao: initialize - DONE");}\r
+        return results;\r
+    }\r
+\r
+    @Override\r
+    public <T extends DescriptionElementBase> long countDescriptionElementForTaxon(\r
+            UUID taxonUuid, Set<Feature> features, Class<T> type) {\r
+\r
+        Query query = prepareGetDescriptionElementForTaxon(taxonUuid, features, type, null, null, true);\r
+\r
+        return (Long)query.uniqueResult();\r
+    }\r
+\r
+    /**\r
+     * @param taxon\r
+     * @param features\r
+     * @param type\r
+     * @param pageSize\r
+     * @param pageNumber\r
+     * @return\r
+     */\r
+    private <T extends DescriptionElementBase> Query prepareGetDescriptionElementForTaxon(UUID taxonUuid,\r
+            Set<Feature> features, Class<T> type, Integer pageSize, Integer pageNumber, boolean asCountQuery) {\r
+\r
+        String listOrCount;\r
+        if(asCountQuery){\r
+            listOrCount = "count(de)";\r
+        } else {\r
+            listOrCount = "de";\r
+        }\r
+        String queryString = "select " + listOrCount +\r
+            " from TaxonDescription as td" +\r
+            " left join td.descriptionElements as de" +\r
+            " where td.taxon.uuid = :taxon_uuid ";\r
+\r
+        if(type != null){\r
+            queryString += " and de.class = :type";\r
+        }\r
+        if (features != null && features.size() > 0){\r
+            queryString += " and de.feature in (:features) ";\r
+        }\r
+//             System.out.println(queryString);\r
+        Query query = getSession().createQuery(queryString);\r
+\r
+        query.setParameter("taxon_uuid", taxonUuid);\r
+        if(type != null){\r
+            query.setParameter("type", type.getSimpleName());\r
+        }\r
+        if(features != null && features.size() > 0){\r
+            query.setParameterList("features", features) ;\r
+        }\r
+\r
+        if(pageSize != null) {\r
+            query.setMaxResults(pageSize);\r
+            if(pageNumber != null) {\r
+                query.setFirstResult(pageNumber * pageSize);\r
+            }\r
+        }\r
+        return query;\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao#listTaxonDescriptionMedia(java.util.UUID, java.lang.Boolean, java.util.Set, java.lang.Integer, java.lang.Integer, java.util.List)\r
+     */\r
+    @Override\r
+    public List<Media> listTaxonDescriptionMedia(UUID taxonUuid,\r
+            Boolean limitToGalleries, Set<MarkerType> markerTypes,\r
+            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+            if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+                String queryString = " SELECT media " +\r
+                    getTaxonDescriptionMediaQueryString(\r
+                        taxonUuid, limitToGalleries,  markerTypes);\r
+                queryString +=\r
+                    " GROUP BY media "\r
+//                                                     " ORDER BY index(media) "  //not functional\r
+                    ;\r
+\r
+                Query query = getSession().createQuery(queryString);\r
+\r
+                setTaxonDescriptionMediaParameters(query, taxonUuid, limitToGalleries, markerTypes);\r
+\r
+\r
+//                 addMarkerTypesCriterion(markerTypes, hql);\r
+\r
+                if(pageSize != null) {\r
+                    query.setMaxResults(pageSize);\r
+                    if(pageNumber != null) {\r
+                        query.setFirstResult(pageNumber * pageSize);\r
+                    }\r
+                }\r
+\r
+                List<Media> results = query.list();\r
+\r
+                defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+                return results;\r
+            } else {\r
+                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptionMedia(UUID taxonUuid, boolean restrictToGalleries)");\r
+            }\r
+    }\r
+\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao#countTaxonDescriptionMedia(java.util.UUID, java.lang.Boolean, java.util.Set)\r
+     */\r
+    @Override\r
+    public int countTaxonDescriptionMedia(UUID taxonUuid,\r
+            Boolean limitToGalleries, Set<MarkerType> markerTypes) {\r
+        AuditEvent auditEvent = getAuditEventFromContext();\r
+        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            String queryString = " SELECT count(DISTINCT media) " +\r
+                getTaxonDescriptionMediaQueryString(\r
+                    taxonUuid, limitToGalleries, markerTypes);\r
+\r
+            Query query = getSession().createQuery(queryString);\r
+            setTaxonDescriptionMediaParameters(query, taxonUuid, limitToGalleries, markerTypes);\r
+            return ((Long)query.uniqueResult()).intValue();\r
+        }else{\r
+            throw new OperationNotSupportedInPriorViewException("countTaxonDescriptionMedia(UUID taxonUuid)");\r
+        }\r
+\r
+    }\r
+\r
+    private void setTaxonDescriptionMediaParameters(Query query, UUID taxonUuid, Boolean limitToGalleries, Set<MarkerType> markerTypes) {\r
+        if(taxonUuid != null){\r
+            query.setParameter("uuid", taxonUuid);\r
+        }\r
+\r
+    }\r
+\r
+    /**\r
+     * @param taxonUuid\r
+     * @param restrictToGalleries\r
+     * @param markerTypes\r
+     * @return\r
+     */\r
+    private String getTaxonDescriptionMediaQueryString(UUID taxonUuid,\r
+            Boolean restrictToGalleries, Set<MarkerType> markerTypes) {\r
+        String fromQueryString =\r
+            " FROM DescriptionElementBase as deb INNER JOIN " +\r
+                " deb.inDescription as td "\r
+                + " INNER JOIN td.taxon as t "\r
+                + " JOIN deb.media as media "\r
+                + " LEFT JOIN td.markers marker ";\r
+\r
+        String whereQueryString = " WHERE (1=1) ";\r
+        if (taxonUuid != null){\r
+            whereQueryString += " AND t.uuid = :uuid ";\r
+        }\r
+        if (restrictToGalleries){\r
+            whereQueryString += " AND td.imageGallery is true ";\r
+        }\r
+        if (markerTypes != null && !markerTypes.isEmpty()){\r
+            whereQueryString += " AND (1=0";\r
+            for (MarkerType markerType : markerTypes){\r
+                whereQueryString += " OR ( marker.markerType.id = " + markerType.getId() + " AND marker.flag is true)";\r
+\r
+            }\r
+            whereQueryString += ") ";\r
+        }\r
+\r
+        return fromQueryString + whereQueryString;\r
+\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao#listNamedAreasInUse(java.lang.Integer, java.lang.Integer, java.util.List)\r
+     */\r
+    @Override\r
+    public List<NamedArea> listNamedAreasInUse(Integer pageSize, Integer pageNumber, List<String> propertyPaths) {\r
+\r
+        String queryString = "select distinct d.area from Distribution as d";\r
+        Query query = getSession().createQuery(queryString);\r
+\r
+        if(pageSize != null) {\r
+            query.setMaxResults(pageSize);\r
+            if(pageNumber != null) {\r
+                query.setFirstResult(pageNumber * pageSize);\r
+            }\r
+        }\r
+\r
+        List<NamedArea> results = query.list();\r
+\r
+        defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+\r
+        return results;\r
+    }\r
+\r
+\r
+\r
 }\r