TaxonDaoTest is running again
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / taxon / TaxonDaoHibernateImpl.java
index 1a02c3dfc4e2fc984981c8950db8387715e383aa..e0931da24674081ac49de7018201680d44c6002c 100644 (file)
@@ -8,25 +8,31 @@
  */\r
 package eu.etaxonomy.cdm.persistence.dao.hibernate.taxon;\r
 \r
+import java.lang.reflect.Field;\r
 import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Comparator;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
 import java.util.List;\r
 import java.util.Set;\r
+import java.util.SortedSet;\r
+import java.util.TreeSet;\r
 import java.util.UUID;\r
 \r
 import org.apache.log4j.Logger;\r
 import org.apache.lucene.analysis.SimpleAnalyzer;\r
 import org.apache.lucene.queryParser.ParseException;\r
 import org.apache.lucene.queryParser.QueryParser;\r
-import org.apache.lucene.search.Sort;\r
-import org.apache.lucene.search.SortField;\r
 import org.hibernate.Criteria;\r
 import org.hibernate.FetchMode;\r
 import org.hibernate.Hibernate;\r
-import org.hibernate.LazyInitializationException;\r
 import org.hibernate.Query;\r
 import org.hibernate.criterion.Criterion;\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.hibernate.search.FullTextSession;\r
 import org.hibernate.search.Search;\r
 import org.hibernate.search.SearchFactory;\r
@@ -34,13 +40,19 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;\r
 import org.springframework.dao.DataAccessException;\r
 import org.springframework.stereotype.Repository;\r
+import org.springframework.util.ReflectionUtils;\r
 \r
 import eu.etaxonomy.cdm.model.common.Annotation;\r
+import eu.etaxonomy.cdm.model.common.Extension;\r
 import eu.etaxonomy.cdm.model.common.Marker;\r
 import eu.etaxonomy.cdm.model.common.OriginalSource;\r
 import eu.etaxonomy.cdm.model.common.RelationshipBase;\r
+import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;\r
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.location.NamedArea;\r
+import eu.etaxonomy.cdm.model.media.Rights;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
 import eu.etaxonomy.cdm.model.reference.ReferenceBase;\r
 import eu.etaxonomy.cdm.model.taxon.Synonym;\r
 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;\r
@@ -49,12 +61,15 @@ import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;\r
+import eu.etaxonomy.cdm.model.view.AuditEvent;\r
 import eu.etaxonomy.cdm.persistence.dao.QueryParseException;\r
-import eu.etaxonomy.cdm.persistence.dao.common.ITitledDao;\r
 import eu.etaxonomy.cdm.persistence.dao.hibernate.AlternativeSpellingSuggestionParser;\r
 import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;\r
 import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;\r
 import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
+\r
 \r
 /**\r
  * @author a.mueller\r
@@ -66,16 +81,24 @@ import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
 public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implements ITaxonDao {       \r
        private AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser;\r
        \r
+       @SuppressWarnings("unused")\r
        private static final Logger logger = Logger.getLogger(TaxonDaoHibernateImpl.class);\r
+               \r
+       private String defaultField = "name.titleCache";\r
+       private Class<? extends TaxonBase> indexedClasses[]; \r
 \r
        public TaxonDaoHibernateImpl() {\r
                super(TaxonBase.class);\r
+               indexedClasses = new Class[2];\r
+               indexedClasses[0] = Taxon.class;\r
+               indexedClasses[1] = Synonym.class;\r
        }\r
        \r
        @Autowired(required = false)   //TODO switched of because it caused problems when starting CdmApplicationController\r
        public void setAlternativeSpellingSuggestionParser(AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser) {\r
-               this.alternativeSpellingSuggestionParser = alternativeSpellingSuggestionParser;\r
+               this.alternativeSpellingSuggestionParser = alternativeSpellingSuggestionParser; \r
        }\r
+       \r
 \r
        /* (non-Javadoc)\r
         * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase)\r
@@ -83,13 +106,12 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
        public List<Taxon> getRootTaxa(ReferenceBase sec) {\r
                return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true, false);\r
        }\r
-       \r
-\r
-\r
+               \r
        /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, eu.etaxonomy.cdm.persistence.fetch.CdmFetch, java.lang.Boolean, java.lang.Boolean)\r
+        * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.ReferenceBase, eu.etaxonomy.cdm.persistence.fetch.CdmFetch, java.lang.Boolean, java.lang.Boolean)\r
         */\r
-       public List<Taxon> getRootTaxa(ReferenceBase sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications) {\r
+       public List<Taxon> getRootTaxa(Rank rank, ReferenceBase sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications, List<String> propertyPaths) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.getRootTaxa(Rank rank, ReferenceBase sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications)");\r
                if (onlyWithChildren == null){\r
                        onlyWithChildren = true;\r
                }\r
@@ -100,37 +122,46 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                        cdmFetch = CdmFetch.NO_FETCH();\r
                }\r
 \r
-\r
-//             String query = "from Taxon root ";\r
-//             query += " where root.taxonomicParentCache is NULL ";\r
-//             if (sec != null){\r
-//             query += " AND root.sec.id = :sec "; \r
-//             }               \r
-//             Query q = getSession().createQuery(query);\r
-//             if (sec != null){\r
-//             q.setInteger("sec", sec.getId());\r
-//             }\r
-\r
-\r
                Criteria crit = getSession().createCriteria(Taxon.class);\r
-               crit.add(Restrictions.isNull("taxonomicParentCache"));\r
+               \r
+               crit.setFetchMode("name", FetchMode.JOIN);\r
+               crit.createAlias("name", "name");\r
+               \r
+               if (rank != null) {\r
+                       crit.add(Restrictions.eq("name.rank", rank));\r
+               }else{\r
+                       crit.add(Restrictions.isNull("taxonomicParentCache"));\r
+               }\r
+\r
                if (sec != null){\r
                        crit.add(Restrictions.eq("sec", sec) );\r
                }\r
 \r
-\r
                if (! cdmFetch.includes(CdmFetch.FETCH_CHILDTAXA())){\r
-                       logger.warn("no child taxa fetch");\r
+                       logger.info("Not fetching child taxa");\r
                        //TODO overwrite LAZY (SELECT) does not work (bug in hibernate?)\r
                        crit.setFetchMode("relationsToThisTaxon.fromTaxon", FetchMode.LAZY);\r
                }\r
 \r
                List<Taxon> results = new ArrayList<Taxon>();\r
-               for(Taxon taxon : (List<Taxon>) crit.list()){\r
+               List<Taxon> taxa = crit.list();\r
+               for(Taxon taxon : taxa){\r
+                       \r
+                       \r
                        //childTaxa\r
                        //TODO create restriction instead\r
+                       // (a) not using cache fields\r
+                       /*Hibernate.initialize(taxon.getRelationsFromThisTaxon());\r
+                       if (onlyWithChildren == false || taxon.getRelationsFromThisTaxon().size() > 0){\r
+                               if (withMisapplications == true || ! taxon.isMisappliedName()){\r
+                                       defaultBeanInitializer.initialize(taxon, propertyPaths);\r
+                                       results.add(taxon);\r
+                               }\r
+                       }*/\r
+                       // (b) using cache fields\r
                        if (onlyWithChildren == false || taxon.hasTaxonomicChildren()){\r
                                if (withMisapplications == true || ! taxon.isMisappliedName()){\r
+                                       defaultBeanInitializer.initialize(taxon, propertyPaths);\r
                                        results.add(taxon);\r
                                }\r
                        }\r
@@ -138,118 +169,369 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                return results;\r
        }\r
 \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getRootTaxa(eu.etaxonomy.cdm.model.reference.ReferenceBase, eu.etaxonomy.cdm.persistence.fetch.CdmFetch, java.lang.Boolean, java.lang.Boolean)\r
+        */\r
+       public List<Taxon> getRootTaxa(ReferenceBase sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications) {\r
+               return getRootTaxa(null, sec, cdmFetch, onlyWithChildren, withMisapplications, null);\r
+       }\r
+       \r
+\r
        public List<TaxonBase> getTaxaByName(String queryString, ReferenceBase sec) {\r
                \r
                return getTaxaByName(queryString, true, sec);\r
        }\r
 \r
        public List<TaxonBase> getTaxaByName(String queryString, Boolean accepted, ReferenceBase sec) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.getTaxaByName(String name, ReferenceBase sec)");\r
                \r
-               Criteria criteria = null;\r
+        Criteria criteria = null;\r
                if (accepted == true) {\r
                        criteria = getSession().createCriteria(Taxon.class);\r
                } else {\r
                        criteria = getSession().createCriteria(Synonym.class);\r
                }\r
-\r
+               \r
                criteria.setFetchMode( "name", FetchMode.JOIN );\r
                criteria.createAlias("name", "name");\r
-\r
-               if (sec != null){\r
-                       if(sec.getId() == 0){\r
-                               getSession().save(sec);\r
-                       }\r
+               \r
+               if (sec != null && sec.getId() != 0) {\r
                        criteria.add(Restrictions.eq("sec", sec ) );\r
                }\r
+\r
                if (queryString != null) {\r
                        criteria.add(Restrictions.ilike("name.nameCache", queryString));\r
                }\r
-               List<TaxonBase> results = criteria.list();\r
+\r
+               return (List<TaxonBase>)criteria.list();\r
+       }\r
+\r
+       public List<TaxonBase> getTaxaByName(Class<? extends TaxonBase> clazz, String queryString, MatchMode matchMode,\r
+                       Integer pageSize, Integer pageNumber) {\r
+               \r
+               return getTaxaByName(clazz, queryString, matchMode, null, null, pageSize, pageNumber, null);\r
+       }\r
+       \r
+       public List<TaxonBase> getTaxaByName(String queryString, MatchMode matchMode, \r
+                       Boolean accepted, Integer pageSize, Integer pageNumber) {\r
+               \r
+               if (accepted == true) {\r
+                       return getTaxaByName(Taxon.class, queryString, matchMode, pageSize, pageNumber);\r
+               } else {\r
+                       return getTaxaByName(Synonym.class, queryString, matchMode, pageSize, pageNumber);\r
+               }\r
+       }\r
+       \r
+       \r
+       public List<TaxonBase> getTaxaByName(Class<? extends TaxonBase> clazz, String queryString, MatchMode matchMode,\r
+                       ReferenceBase sec, Set<NamedArea> namedAreas, Integer pageSize, \r
+                       Integer pageNumber, List<String> propertyPaths) {\r
+                               \r
+               \r
+               boolean doCount = false;\r
+               \r
+               Query query = prepareTaxaByName(clazz, queryString, matchMode, namedAreas, pageSize, pageNumber, doCount);\r
+\r
+               List<TaxonBase> results = query.list();\r
+               \r
+               defaultBeanInitializer.initializeAll(results, propertyPaths);\r
+               \r
                return results;\r
+               \r
+       }\r
+\r
+       /**\r
+        * @param clazz\r
+        * @param queryString\r
+        * @param matchMode\r
+        * @param namedAreas\r
+        * @param pageSize\r
+        * @param pageNumber\r
+        * @param doCount\r
+        * @return\r
+        * \r
+        * FIXME implement taxontree restriction & implement test: see {@link TaxonDaoHibernateImplTest#testCountTaxaByName()}\r
+        */\r
+       private Query prepareTaxaByName(Class<? extends TaxonBase> clazz, String queryString, MatchMode matchMode,\r
+                       Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber, boolean doCount) {\r
+       \r
+               \r
+               //TODO ? checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(String queryString, Boolean accepted, ReferenceBase sec)");\r
+\r
+               String hqlQueryString = matchMode.queryStringFrom(queryString);\r
+               \r
+               String matchOperator;\r
+               if (matchMode == MatchMode.EXACT) {\r
+                       matchOperator = "=";\r
+               } else {\r
+                       matchOperator = "like";\r
+               }\r
+               \r
+               String selectWhat = (doCount ? "count(distinct t)": "distinct t");\r
+               \r
+               String hql = "";\r
+               Set<NamedArea> areasExpanded = new HashSet<NamedArea>();\r
+               if(namedAreas != null && namedAreas.size() > 0){\r
+                       // expand areas\r
+                       List<NamedArea> childAreas;\r
+                       Query areaQuery = getSession().createQuery("select childArea from NamedArea as childArea left join childArea.partOf as parentArea where parentArea = :area");\r
+                       expandNamedAreas(namedAreas, areasExpanded, areaQuery);\r
+                       \r
+                       if(clazz.equals(Taxon.class)){\r
+                               hql = "select " + selectWhat + " from Distribution e join e.inDescription d join d.taxon t join t.name n "+\r
+                                       " where e.area in (:namedAreas) AND n.nameCache " + matchOperator + " :queryString";   \r
+                       } else {\r
+                               //FIXME implement\r
+                               logger.warn("find synonyms by area not jet implemented");\r
+                               hql = "select " + selectWhat + " from Distribution e join e.inDescription d join d.taxon t join t.name n "+\r
+                                       " where e.area in (:namedAreas) AND n.nameCache " + matchOperator + " :queryString";   \r
+                       }\r
+               } else {\r
+                       hql = "select " + selectWhat + " from " + clazz.getSimpleName() + " t" \r
+                       + " where t.name.nameCache " + matchOperator + " :queryString";\r
+               }\r
+               \r
+               if(!doCount){\r
+                       hql += " order by t.titleCache"; //" order by t.name.nameCache";\r
+               }\r
+               \r
+               Query query = getSession().createQuery(hql);\r
+               \r
+               query.setParameter("queryString", hqlQueryString);\r
+               if(areasExpanded.size() > 0){\r
+                       query.setParameterList("namedAreas", areasExpanded);\r
+               }\r
+               \r
+               if(pageSize != null &&  !doCount) {\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.taxon.ITaxonDao#countTaxaByName(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, eu.etaxonomy.cdm.persistence.query.SelectMode, eu.etaxonomy.cdm.model.reference.ReferenceBase, java.util.Set)\r
+        */\r
+       public long countTaxaByName(Class<? extends TaxonBase> clazz, String queryString, MatchMode matchMode,\r
+               ReferenceBase sec, Set<NamedArea> namedAreas) {\r
+               \r
+               boolean doCount = true;\r
+               Query query = prepareTaxaByName(clazz, queryString, matchMode, namedAreas, null, null, doCount);\r
+               Object result = query.uniqueResult();\r
+               return (Long) result;\r
+               \r
+       }\r
+\r
+       /**\r
+        * @param namedAreas\r
+        * @param areasExpanded\r
+        * @param areaQuery\r
+        */\r
+       private void expandNamedAreas(Collection<NamedArea> namedAreas, Set<NamedArea> areasExpanded, Query areaQuery) {\r
+               List<NamedArea> childAreas;\r
+               for(NamedArea a : namedAreas){\r
+                       areasExpanded.add(a);\r
+                       areaQuery.setParameter("area", a);\r
+                       childAreas = areaQuery.list();\r
+                       if(childAreas.size() > 0){\r
+                               areasExpanded.addAll(childAreas);\r
+                               expandNamedAreas(childAreas, areasExpanded, areaQuery);\r
+                       }\r
+               }\r
        }\r
+       \r
+//     /* (non-Javadoc)\r
+//      * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countTaxaByName(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, eu.etaxonomy.cdm.persistence.query.SelectMode)\r
+//      */\r
+//     public Integer countTaxaByName(String queryString, MatchMode matchMode, SelectMode selectMode) {                \r
+//             return countTaxaByName(queryString, matchMode, selectMode, null);\r
+//     }\r
+\r
+//     /* (non-Javadoc)\r
+//      * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countTaxaByName(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, eu.etaxonomy.cdm.persistence.query.SelectMode, eu.etaxonomy.cdm.model.reference.ReferenceBase)\r
+//      */\r
+//     public Integer countTaxaByName(String queryString, \r
+//                     MatchMode matchMode, SelectMode selectMode, ReferenceBase sec) {\r
+//\r
+//             Long count = countTaxaByName(queryString, matchMode, selectMode, sec, null);\r
+//             return count.intValue();\r
+//\r
+//     }\r
+       \r
+//     public Integer countTaxaByName(String queryString, MatchMode matchMode, Boolean accepted) {\r
+//             \r
+//             SelectMode selectMode = (accepted ? SelectMode.TAXA : SelectMode.SYNONYMS);\r
+//             Long count = countTaxaByName(queryString, matchMode, selectMode, null, null);\r
+//             return count.intValue();\r
+//     }\r
+       \r
 \r
        public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {\r
-               Criteria crit = getSession().createCriteria(TaxonBase.class);\r
-               List<TaxonBase> results = crit.list();\r
-               // TODO add page & pagesize criteria\r
-               return results;\r
+               return super.list(pagesize, page);\r
        }\r
 \r
        public List<Synonym> getAllSynonyms(Integer limit, Integer start) {\r
-               Criteria crit = getSession().createCriteria(Synonym.class);\r
-               List<Synonym> results = crit.list();\r
-               return results;\r
+               return super.list(Synonym.class, limit, start);\r
        }\r
 \r
        public List<Taxon> getAllTaxa(Integer limit, Integer start) {\r
-               Criteria crit = getSession().createCriteria(Taxon.class);\r
-               List<Taxon> results = crit.list();\r
-               return results;\r
+               return super.list(Taxon.class, limit, start);\r
        }\r
 \r
        public List<RelationshipBase> getAllRelationships(Integer limit, Integer start) {\r
-               Criteria crit = getSession().createCriteria(RelationshipBase.class);\r
-               List<RelationshipBase> results = crit.list();\r
-               return results;\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+               if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+                   Criteria criteria = getSession().createCriteria(RelationshipBase.class);\r
+                   return (List<RelationshipBase>)criteria.list();\r
+               } else {\r
+                       AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(RelationshipBase.class,auditEvent.getRevisionNumber());\r
+                       return (List<RelationshipBase>)query.getResultList();\r
+               }\r
        }\r
-\r
+       \r
+       /** Sets the taxonomic parent to null. Does not handle taxonomic relationships. */\r
+//     private boolean nullifyTaxonomicParent(Taxon taxon) {\r
+//\r
+//             try {\r
+//                     Method nullifyTaxonomicParent = taxon.getClass().getMethod("nullifyTaxonomicParent");\r
+//                     nullifyTaxonomicParent.invoke(taxon);\r
+//             } catch (NoSuchMethodException ex) {\r
+//                     logger.error("NoSuchMethod: " + ex.getMessage());\r
+//                     return false;\r
+//             } catch (IllegalArgumentException ex) {\r
+//                     logger.error("IllegalArgumentException: " + ex.getMessage());\r
+//                     return false;\r
+//             } catch (IllegalAccessException ex) {\r
+//                     logger.error("IllegalAccessException: " + ex.getMessage());\r
+//                     return false;\r
+//             } catch (InvocationTargetException ex) {\r
+//                     logger.error("IllegalAccessException: " + ex.getMessage());\r
+//                     return false;\r
+//             }\r
+//             return true;\r
+//     }\r
+       \r
        @Override\r
        public UUID delete(TaxonBase taxonBase) throws DataAccessException{\r
-               //getSession().update(taxonBase); doesn't work with lazy collections\r
                if (taxonBase == null){\r
                        logger.warn("TaxonBase was 'null'");\r
                        return null;\r
                }\r
-               //annotations\r
-               try {\r
-                       Set<Annotation> annotations = taxonBase.getAnnotations();\r
-                       for (Annotation annotation: annotations){\r
-                               taxonBase.removeAnnotation(annotation);\r
-                       }\r
-               } catch (LazyInitializationException e) {\r
-                       logger.warn("LazyInitializationException: " + e);\r
-               }\r
-               //markers\r
-               try {\r
-                       Set<Marker> markers = taxonBase.getMarkers();\r
-                       for (Marker marker: markers){\r
-                               taxonBase.removeMarker(marker);\r
-                       }\r
-               } catch (LazyInitializationException e) {\r
-                       logger.warn("LazyInitializationException: " + e);\r
-               }\r
-               //originalSource\r
-               try {\r
-                       Set<OriginalSource> origSources = taxonBase.getSources();\r
-                       for (OriginalSource source: origSources){\r
-                               taxonBase.removeSource(source);\r
-                       }\r
-               } catch (LazyInitializationException e) {\r
-                       logger.warn("LazyInitializationException: " + e);\r
-               }\r
-               //is Taxon\r
-               TaxonNameBase taxonNameBase =taxonBase.getName();\r
-               if (taxonNameBase != null){\r
-                       taxonNameBase.removeTaxonBase(taxonBase);\r
-               }\r
-               if (taxonBase instanceof Taxon){\r
+               \r
+               // Merge the object in if it is detached\r
+               //\r
+               // I think this is preferable to catching lazy initialization errors \r
+               // as that solution only swallows and hides the exception, but doesn't \r
+               // actually solve it.\r
+               getSession().merge(taxonBase);\r
+               \r
+               for(Iterator<Annotation> iterator = taxonBase.getAnnotations().iterator(); iterator.hasNext();) {\r
+                       Annotation annotation = iterator.next();\r
+                   annotation.setAnnotatedObj(null);\r
+                   iterator.remove();\r
+                   getSession().delete(annotation);\r
+           }\r
+               \r
+               for(Iterator<Marker> iterator = taxonBase.getMarkers().iterator(); iterator.hasNext();) {\r
+                       Marker marker = iterator.next();\r
+                   marker.setMarkedObj(null);\r
+                   iterator.remove();\r
+                   getSession().delete(marker);\r
+           }\r
+               \r
+               for(Iterator<Extension> iterator = taxonBase.getExtensions().iterator(); iterator.hasNext();) {\r
+                       Extension extension = iterator.next();\r
+                   extension.setExtendedObj(null);\r
+                   iterator.remove();\r
+                   getSession().delete(extension);\r
+           }\r
+               \r
+               for(Iterator<OriginalSource> iterator = taxonBase.getSources().iterator(); iterator.hasNext();) {\r
+                       OriginalSource source = iterator.next();\r
+                   source.setSourcedObj(null);\r
+                   iterator.remove();\r
+                   getSession().delete(source);\r
+           }\r
+\r
+               for(Iterator<Rights> iterator = taxonBase.getRights().iterator(); iterator.hasNext();) {\r
+                       Rights rights = iterator.next();\r
+                   iterator.remove();\r
+                   getSession().delete(rights);\r
+           }\r
+               \r
+               if (taxonBase instanceof Taxon){ //     is Taxon\r
                        //taxonRelationships\r
                        Taxon taxon = (Taxon)taxonBase;\r
-                       Set<TaxonRelationship> taxRels = taxon.getTaxonRelations();\r
-                       for (TaxonRelationship taxRel: taxRels){\r
-                               taxon.removeTaxonRelation(taxRel);\r
-                       } ;\r
+                                               \r
+                       for (Iterator<TaxonRelationship> iterator = taxon.getRelationsFromThisTaxon().iterator(); iterator.hasNext();){\r
+                               TaxonRelationship relationFromThisTaxon = iterator.next();\r
+                               iterator.remove();\r
+                               \r
+                               // decrease children count of taxonomic parent by one\r
+                               if (relationFromThisTaxon.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())) {\r
+                                       Taxon toTaxon = relationFromThisTaxon.getToTaxon(); // parent\r
+                                       if (toTaxon != null) {\r
+                                               toTaxon.setTaxonomicChildrenCount(toTaxon.getTaxonomicChildrenCount() - 1);     \r
+                                       }\r
+                               }\r
+                               relationFromThisTaxon.setToTaxon(null);\r
+                               relationFromThisTaxon.setFromTaxon(null);\r
+                               getSession().delete(relationFromThisTaxon);\r
+                       }\r
+                       \r
+                       for (Iterator<TaxonRelationship> iterator = taxon.getRelationsToThisTaxon().iterator(); iterator.hasNext();){\r
+                               TaxonRelationship relationToThisTaxon = iterator.next();\r
+                               iterator.remove();\r
+                               \r
+                // set parent cache of child to null\r
+                               if (relationToThisTaxon.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())) {\r
+                                       Taxon fromTaxon = relationToThisTaxon.getFromTaxon(); // child\r
+                                       if (fromTaxon != null) {\r
+                                               fromTaxon.nullifyTaxonomicParent();\r
+                                       }\r
+                               }\r
+                               relationToThisTaxon.setFromTaxon(null);\r
+                               relationToThisTaxon.setToTaxon(null);\r
+                               getSession().delete(relationToThisTaxon);\r
+                       }\r
+                       \r
                        //SynonymRelationships\r
-                       Set<SynonymRelationship> synRels = taxon.getSynonymRelations();\r
-                       for (SynonymRelationship synRel: synRels){\r
-                               taxon.removeSynonymRelation(synRel);\r
-                       } ;\r
-               }//is Synonym\r
-               else if (taxonBase instanceof Synonym){\r
+                       for (Iterator<SynonymRelationship> iterator = taxon.getSynonymRelations().iterator(); iterator.hasNext();){\r
+                               SynonymRelationship synonymRelation = iterator.next();\r
+                               iterator.remove();\r
+                               synonymRelation.setAcceptedTaxon(null);\r
+                               synonymRelation.setSynonym(null);\r
+                               getSession().delete(synonymRelation);\r
+                       } \r
+                       \r
+                       // Descriptions\r
+                       for (Iterator<TaxonDescription> iterDesc = taxon.getDescriptions().iterator(); iterDesc.hasNext();) {\r
+                               TaxonDescription taxonDescription = iterDesc.next();\r
+                               iterDesc.remove();\r
+                               //taxonDescription.setTaxon(null);\r
+                               Field field = ReflectionUtils.findField(TaxonDescription.class, "taxon", Taxon.class);\r
+                               ReflectionUtils.makeAccessible(field);\r
+                               ReflectionUtils.setField(field, taxonDescription, null);\r
+                               for (Iterator<DescriptionElementBase> iterDescElem = \r
+                                       taxonDescription.getElements().iterator(); iterDescElem.hasNext();) {\r
+                                       DescriptionElementBase descriptionElement = iterDescElem.next();\r
+                                       iterDescElem.remove();\r
+                                       getSession().delete(descriptionElement);\r
+                               }\r
+                               getSession().delete(taxonDescription);\r
+                       }\r
+                       \r
+                       taxon.nullifyTaxonomicParent();\r
+\r
+               } else { //is Synonym\r
                        Synonym synonym = (Synonym)taxonBase;\r
-                       Set<SynonymRelationship> synRels = synonym.getSynonymRelations();\r
-                       for (SynonymRelationship synRel: synRels){\r
-                               synonym.removeSynonymRelation(synRel);\r
+                       for (Iterator<SynonymRelationship> iterator = synonym.getSynonymRelations().iterator(); iterator.hasNext();){\r
+                               SynonymRelationship synonymRelation = iterator.next();\r
+                               iterator.remove();\r
+                               synonymRelation.setAcceptedTaxon(null);\r
+                               synonymRelation.setSynonym(null);\r
                        } ;\r
                }\r
                return super.delete(taxonBase);\r
@@ -257,27 +539,27 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
 \r
 \r
        // TODO add generic return type !!\r
-       public List findByName(String queryString, ITitledDao.MATCH_MODE matchMode, int page, int pagesize, boolean onlyAcccepted) {\r
+       public List findByName(String queryString, MatchMode matchMode, int page, int pagesize, boolean onlyAcccepted) {\r
                ArrayList<Criterion> criteria = new ArrayList<Criterion>();\r
                //TODO ... Restrictions.eq(propertyName, value)\r
                return super.findByTitle(queryString, matchMode, page, pagesize, criteria);\r
 \r
        }\r
 \r
-       public int countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted) {\r
-\r
+       public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted)");\r
                Criteria crit = getSession().createCriteria(type);\r
-               crit.add(Restrictions.ilike("persistentTitleCache", matchMode.queryStringFrom(queryString)));\r
+               crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));\r
                crit.setProjection(Projections.rowCount());\r
                int result = ((Integer)crit.list().get(0)).intValue();\r
                return result;\r
        }\r
 \r
 \r
-       public int countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria) {\r
-\r
+       public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");\r
                Criteria crit = getSession().createCriteria(type);\r
-               crit.add(Restrictions.ilike("persistentTitleCache", matchMode.queryStringFrom(queryString)));\r
+               crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));\r
                if(criteria != null){\r
                        for (Criterion criterion : criteria) {\r
                                crit.add(criterion);\r
@@ -288,53 +570,75 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                return result;\r
        }\r
 \r
-       public int countRelatedTaxa(Taxon taxon, TaxonRelationshipType type) {\r
-               Query query = null;\r
+       public int countTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Direction direction) {\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+               if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+                   Query query = null;\r
+               \r
+                   if(type == null) {\r
+                           query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");\r
+                   } else {\r
+                           query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");\r
+                           query.setParameter("type",type);\r
+                   }\r
+                   query.setParameter("relatedTaxon", taxon);\r
                \r
-               if(type == null) {\r
-                       query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship.relatedTo = :relatedTo");\r
+                   return ((Long)query.uniqueResult()).intValue();\r
                } else {\r
-                       query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship.relatedTo = :relatedTo and taxonRelationship.type = :type");\r
-                       query.setParameter("type",type);\r
+                       AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());\r
+                       query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));\r
+                       query.addProjection(AuditEntity.id().count("id"));\r
+                       \r
+                       if(type != null) {\r
+                               query.add(AuditEntity.relatedId("type").eq(type.getId()));\r
+                   }\r
+                       \r
+                       return ((Long)query.getSingleResult()).intValue();\r
                }\r
-               \r
-               query.setParameter("relatedTo", taxon);\r
-               \r
-               return ((Long)query.uniqueResult()).intValue();\r
        }\r
 \r
        public int countSynonyms(Taxon taxon, SynonymRelationshipType type) {\r
-        Query query = null;\r
-               \r
-               if(type == null) {\r
-                       query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo");\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+               if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+                       Query query = null;\r
+\r
+                       if(type == null) {\r
+                               query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo");\r
+                       } else {\r
+                               query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo and synonymRelationship.type = :type");\r
+                               query.setParameter("type",type);\r
+                       }\r
+\r
+                       query.setParameter("relatedTo", taxon);\r
+\r
+                       return ((Long)query.uniqueResult()).intValue();\r
                } else {\r
-                       query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship.relatedTo = :relatedTo and synonymRelationship.type = :type");\r
-                       query.setParameter("type",type);\r
+                       AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());\r
+                       query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));\r
+                       query.addProjection(AuditEntity.id().count("id"));\r
+                       \r
+                       if(type != null) {\r
+                               query.add(AuditEntity.relatedId("type").eq(type.getId()));\r
+                   }\r
+                       \r
+                       return ((Long)query.getSingleResult()).intValue();\r
                }\r
-               \r
-               query.setParameter("relatedTo", taxon);\r
-               \r
-               return ((Long)query.uniqueResult()).intValue();\r
        }\r
 \r
-       public int countTaxa(String queryString, Boolean accepted) {\r
-        QueryParser queryParser = new QueryParser("name.persistentTitleCache", new SimpleAnalyzer());\r
+       public int count(Class<? extends TaxonBase> clazz, String queryString) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.count(String queryString, Boolean accepted)");\r
+        QueryParser queryParser = new QueryParser(defaultField, new SimpleAnalyzer());\r
                \r
                try {\r
                        org.apache.lucene.search.Query query = queryParser.parse(queryString);\r
                        \r
-                       FullTextSession fullTextSession = Search.createFullTextSession(this.getSession());\r
+                       FullTextSession fullTextSession = Search.getFullTextSession(this.getSession());\r
                        org.hibernate.search.FullTextQuery fullTextQuery = null;\r
                        \r
-                       if(accepted == null) {\r
-                               fullTextQuery = fullTextSession.createFullTextQuery(query, TaxonBase.class);\r
+                       if(clazz == null) {\r
+                               fullTextQuery = fullTextSession.createFullTextQuery(query, type);\r
                        } else {\r
-                               if(accepted) {\r
-                                       fullTextQuery = fullTextSession.createFullTextQuery(query, Taxon.class);\r
-                               } else {\r
-                                       fullTextQuery = fullTextSession.createFullTextQuery(query, Synonym.class);\r
-                               }\r
+                               fullTextQuery = fullTextSession.createFullTextQuery(query, clazz);\r
                        }\r
                        \r
                    Integer  result = fullTextQuery.getResultSize();\r
@@ -345,44 +649,22 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                }\r
        }\r
        \r
-       public int countTaxaByName(String queryString, Boolean accepted, ReferenceBase sec) {\r
-               \r
-               Criteria criteria = null;\r
-               \r
-               if (accepted == true) {\r
-                       criteria = getSession().createCriteria(Taxon.class);\r
-               } else {\r
-                       criteria = getSession().createCriteria(Synonym.class);\r
-               }\r
-               \r
-               criteria.setFetchMode( "name", FetchMode.JOIN );\r
-               criteria.createAlias("name", "name");\r
-\r
-               if (sec != null){\r
-                       if(sec.getId() == 0){\r
-                               getSession().save(sec);\r
-                       }\r
-                       criteria.add(Restrictions.eq("sec", sec ) );\r
-               }\r
-               if (queryString != null) {\r
-                       criteria.add(Restrictions.ilike("name.nameCache", queryString));\r
-               }\r
-               criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));\r
-               \r
-               return (Integer)criteria.uniqueResult();\r
-       }\r
-\r
-       public int countTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,       String infraSpecificEpithet, Rank rank) {\r
+//     public int countTaxaByName(String queryString, Boolean accepted, ReferenceBase sec) {\r
+//             \r
+//             SelectMode selectMode = (accepted ? SelectMode.TAXA : SelectMode.SYNONYMS);\r
+//             Long count = countTaxaByName(queryString, MatchMode.ANYWHERE, selectMode , sec, null);\r
+//             \r
+//             return count.intValue();\r
+//     }\r
+\r
+       public int countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,       String infraSpecificEpithet, Rank rank) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,   String infraGenericEpithet, String specificEpithet,     String infraSpecificEpithet, Rank rank)");\r
         Criteria criteria = null;\r
                \r
-               if(accepted == null) {\r
+               if(clazz == null) {\r
                        criteria = getSession().createCriteria(TaxonBase.class);\r
                } else {\r
-                       if(accepted) {\r
-                               criteria = getSession().createCriteria(Taxon.class);\r
-                       } else {\r
-                               criteria = getSession().createCriteria(Synonym.class);\r
-                       }\r
+                       criteria = getSession().createCriteria(clazz);          \r
                }\r
                \r
                criteria.setFetchMode( "name", FetchMode.JOIN );\r
@@ -413,37 +695,41 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                return (Integer)criteria.uniqueResult();\r
        }\r
 \r
-       public List<TaxonBase> findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,  Integer pageNumber) {\r
+       public List<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,  Integer pageNumber) {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,      Integer pageNumber)");\r
                Criteria criteria = null;\r
                \r
-               if(accepted == null) {\r
+               if(clazz == null) {\r
                        criteria = getSession().createCriteria(TaxonBase.class);\r
                } else {\r
-                       if(accepted) {\r
-                               criteria = getSession().createCriteria(Taxon.class);\r
-                       } else {\r
-                               criteria = getSession().createCriteria(Synonym.class);\r
-                       }\r
+                       criteria = getSession().createCriteria(clazz);\r
                }\r
                \r
                criteria.setFetchMode( "name", FetchMode.JOIN );\r
                criteria.createAlias("name", "name");\r
                \r
-               if(genusOrUninomial != null) {\r
+               if(genusOrUninomial == null) {\r
+                       criteria.add(Restrictions.isNull("name.genusOrUninomial"));\r
+               } else if(!genusOrUninomial.equals("*")) {\r
                        criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));\r
                }\r
                \r
-               if(infraGenericEpithet != null) {\r
-                       criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));\r
-               } else {\r
+               if(infraGenericEpithet == null) {\r
                        criteria.add(Restrictions.isNull("name.infraGenericEpithet"));\r
-               }\r
+               } else if(!infraGenericEpithet.equals("*")) {\r
+                       criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));\r
+               } \r
                \r
-               if(specificEpithet != null) {\r
+               if(specificEpithet == null) {\r
+                       criteria.add(Restrictions.isNull("name.specificEpithet"));\r
+               } else if(!specificEpithet.equals("*")) {\r
                        criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));\r
+                       \r
                }\r
                \r
-               if(infraSpecificEpithet != null) {\r
+               if(infraSpecificEpithet == null) {\r
+                       criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));\r
+               } else if(!infraSpecificEpithet.equals("*")) {\r
                        criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));\r
                }\r
                \r
@@ -463,84 +749,140 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                return (List<TaxonBase>)criteria.list();\r
        }\r
 \r
-       public List<TaxonRelationship> getRelatedTaxa(Taxon taxon,      TaxonRelationshipType type, Integer pageSize, Integer pageNumber) {\r
-        Query query = null;\r
+       public List<TaxonRelationship> getTaxonRelationships(Taxon taxon,       TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+               if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+                       Criteria criteria = getSession().createCriteria(TaxonRelationship.class);\r
+            \r
+                       criteria.add(Restrictions.eq("relatedTo", taxon));\r
+                   if(type != null) {\r
+                       criteria.add(Restrictions.eq("type", type));\r
+                   } \r
                \r
-               if(type == null) {\r
-                       query = getSession().createQuery("select taxonRelationship from TaxonRelationship taxonRelationship join fetch taxonRelationship.relatedFrom where taxonRelationship.relatedTo = :relatedTo");\r
-               } else {\r
-                       query = getSession().createQuery("select taxonRelationship from TaxonRelationship taxonRelationship join fetch taxonRelationship.relatedFrom where taxonRelationship.relatedTo = :relatedTo and taxonRelationship.type = :type");\r
-                       query.setParameter("type",type);\r
-               }\r
+            addOrder(criteria,orderHints);\r
                \r
-               query.setParameter("relatedTo", taxon);\r
+                   if(pageSize != null) {\r
+                       criteria.setMaxResults(pageSize);\r
+                       if(pageNumber != null) {\r
+                               criteria.setFirstResult(pageNumber * pageSize);\r
+                       } else {\r
+                               criteria.setFirstResult(0);\r
+                       }\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
+                   List<TaxonRelationship> result = (List<TaxonRelationship>)criteria.list();\r
+                   defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+                   \r
+                   return result;\r
+               } else {\r
+                       AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());\r
+                       query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));\r
+                       \r
+                       if(type != null) {\r
+                               query.add(AuditEntity.relatedId("type").eq(type.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<TaxonRelationship> result = (List<TaxonRelationship>)query.getResultList();\r
+                       defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+                       \r
+                       // Ugly, but for now, there is no way to sort on a related entity property in Envers,\r
+                       // and we can't live without this functionality in CATE as it screws up the whole \r
+                       // taxon tree thing\r
+                       if(orderHints != null && !orderHints.isEmpty()) {\r
+                           SortedSet<TaxonRelationship> sortedList = new TreeSet<TaxonRelationship>(new TaxonRelationshipFromTaxonComparator());\r
+                           sortedList.addAll(result);\r
+                           return new ArrayList<TaxonRelationship>(sortedList);\r
+                       }\r
+                       \r
+                       return result;\r
+               }\r
+       }\r
+       \r
+       class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {\r
+\r
+               public int compare(TaxonRelationship o1, TaxonRelationship o2) {\r
+                       return o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());\r
                }\r
                \r
-               return (List<TaxonRelationship>)query.list();\r
        }\r
 \r
-       public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber) {\r
-        Query query = null;\r
+       public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+               AuditEvent auditEvent = getAuditEventFromContext();\r
+               if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {\r
+            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);\r
+            \r
+                       criteria.add(Restrictions.eq("relatedTo", taxon));\r
+                   if(type != null) {\r
+                       criteria.add(Restrictions.eq("type", type));\r
+                   } \r
                \r
-               if(type == null) {\r
-                       query = getSession().createQuery("select synonymRelationship from SynonymRelationship synonymRelationship join fetch synonymRelationship.relatedFrom where synonymRelationship.relatedTo = :relatedTo");\r
-               } else {\r
-                       query = getSession().createQuery("select synonymRelationship from SynonymRelationship synonymRelationship join fetch synonymRelationship.relatedFrom where synonymRelationship.relatedTo = :relatedTo and synonymRelationship.type = :type");\r
-                       query.setParameter("type",type);\r
-               }\r
+            addOrder(criteria,orderHints);\r
                \r
-               query.setParameter("relatedTo", taxon);\r
+                   if(pageSize != null) {\r
+                       criteria.setMaxResults(pageSize);\r
+                       if(pageNumber != null) {\r
+                               criteria.setFirstResult(pageNumber * pageSize);\r
+                       } else {\r
+                               criteria.setFirstResult(0);\r
+                       }\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
+                   List<SynonymRelationship> result = (List<SynonymRelationship>)criteria.list();\r
+                   defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+                   \r
+                   return result;\r
+               } else {\r
+                       AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());\r
+                       query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));\r
+                       \r
+                       if(type != null) {\r
+                               query.add(AuditEntity.relatedId("type").eq(type.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<SynonymRelationship> result = (List<SynonymRelationship>)query.getResultList();\r
+                       defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+                       \r
+                       return result;\r
                }\r
-               \r
-               return (List<SynonymRelationship>)query.list();\r
        }\r
 \r
-       public List<TaxonBase> searchTaxa(String queryString, Boolean accepted, Integer pageSize, Integer pageNumber) {\r
-               QueryParser queryParser = new QueryParser("name.persistentTitleCache", new SimpleAnalyzer());\r
+       public List<TaxonBase> search(Class<? extends TaxonBase> clazz, String queryString,Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)  {\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.searchTaxa(String queryString, Boolean accepted,     Integer pageSize, Integer pageNumber)");\r
+               QueryParser queryParser = new QueryParser(defaultField, new SimpleAnalyzer());\r
                List<TaxonBase> results = new ArrayList<TaxonBase>();\r
                 \r
                try {\r
                        org.apache.lucene.search.Query query = queryParser.parse(queryString);\r
                        \r
-                       FullTextSession fullTextSession = Search.createFullTextSession(getSession());\r
+                       FullTextSession fullTextSession = Search.getFullTextSession(getSession());\r
                        org.hibernate.search.FullTextQuery fullTextQuery = null;\r
-                       Criteria criteria = null;\r
                        \r
-                       if(accepted == null) {\r
+                       if(clazz == null) {\r
                                fullTextQuery = fullTextSession.createFullTextQuery(query, TaxonBase.class);\r
-                               criteria =  getSession().createCriteria( TaxonBase.class );\r
                        } else {\r
-                               if(accepted) {\r
-                                       fullTextQuery = fullTextSession.createFullTextQuery(query, Taxon.class);\r
-                                       criteria =  getSession().createCriteria( Taxon.class );\r
-                               } else {\r
-                                       fullTextQuery = fullTextSession.createFullTextQuery(query, Synonym.class);\r
-                                       criteria =  getSession().createCriteria( Synonym.class );\r
-                               }\r
+                               fullTextQuery = fullTextSession.createFullTextQuery(query, clazz);\r
                        }\r
                        \r
-                       org.apache.lucene.search.Sort sort = new Sort(new SortField("name.titleCache_forSort"));\r
-                       fullTextQuery.setSort(sort);\r
+                       addOrder(fullTextQuery,orderHints);\r
                        \r
-                       criteria.setFetchMode( "name", FetchMode.JOIN );\r
-                   fullTextQuery.setCriteriaQuery(criteria);\r
-                   \r
                    if(pageSize != null) {\r
                        fullTextQuery.setMaxResults(pageSize);\r
                            if(pageNumber != null) {\r
@@ -550,7 +892,9 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
                            }\r
                        }\r
                    \r
-                   return (List<TaxonBase>)fullTextQuery.list();\r
+                   List<TaxonBase> result = (List<TaxonBase>)fullTextQuery.list();\r
+                   defaultBeanInitializer.initializeAll(result, propertyPaths);\r
+                   return result;\r
 \r
                } catch (ParseException e) {\r
                        throw new QueryParseException(e, queryString);\r
@@ -558,40 +902,49 @@ public class TaxonDaoHibernateImpl extends IdentifiableDaoBase<TaxonBase> implem
        }\r
        \r
        public void purgeIndex() {\r
-               FullTextSession fullTextSession = Search.createFullTextSession(getSession());\r
-               \r
-               fullTextSession.purgeAll(type); // remove all taxon base from indexes\r
-               // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA\r
+               FullTextSession fullTextSession = Search.getFullTextSession(getSession());\r
+               for(Class clazz : indexedClasses) {\r
+                   fullTextSession.purgeAll(clazz); // remove all taxon base from indexes\r
+               }\r
+               fullTextSession.flushToIndexes();\r
        }\r
 \r
        public void rebuildIndex() {\r
-               FullTextSession fullTextSession = Search.createFullTextSession(getSession());\r
+               FullTextSession fullTextSession = Search.getFullTextSession(getSession());\r
                \r
                for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base\r
                        Hibernate.initialize(taxonBase.getName());\r
                        fullTextSession.index(taxonBase);\r
                }\r
-               // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA\r
+               fullTextSession.flushToIndexes();\r
        }\r
        \r
        public void optimizeIndex() {\r
-               FullTextSession fullTextSession = Search.createFullTextSession(getSession());\r
+               FullTextSession fullTextSession = Search.getFullTextSession(getSession());\r
                SearchFactory searchFactory = fullTextSession.getSearchFactory();\r
-           searchFactory.optimize(type); // optimize the indices ()\r
-           // fullTextSession.flushToIndexes() not implemented in 3.0.0.GA\r
+               for(Class clazz : indexedClasses) {\r
+               searchFactory.optimize(clazz); // optimize the indices ()\r
+               }\r
+           fullTextSession.flushToIndexes();\r
        }\r
 \r
        public String suggestQuery(String queryString) {\r
-               try {\r
-                       String alternativeQueryString = null;\r
-                       alternativeSpellingSuggestionParser.parse(queryString);\r
-                       org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);\r
-                       if(alternativeQuery != null) {\r
-                               alternativeQueryString = alternativeQuery.toString("name.persistentTitleCache");\r
+               checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");\r
+               String alternativeQueryString = null;\r
+               if (alternativeSpellingSuggestionParser != null) {\r
+                       try {\r
+\r
+                               alternativeSpellingSuggestionParser.parse(queryString);\r
+                               org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);\r
+                               if (alternativeQuery != null) {\r
+                                       alternativeQueryString = alternativeQuery\r
+                                                       .toString("name.titleCache");\r
+                               }\r
+\r
+                       } catch (ParseException e) {\r
+                               throw new QueryParseException(e, queryString);\r
                        }\r
-                       return alternativeQueryString;\r
-               } catch (ParseException e) {\r
-                       throw new QueryParseException(e, queryString);\r
                }\r
+               return alternativeQueryString;\r
        }\r
-}
\ No newline at end of file
+}\r