allow saving FullDataGenerator data through API (preliminary)
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / CdmGenericDaoImpl.java
index 759c83fdb4682e0067626632849e65cd86ed907d..a4dcdf05a52b9f79bc4ccbfed393982c6eaaef6b 100644 (file)
@@ -21,20 +21,27 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import javax.naming.NamingException;
 import javax.naming.Reference;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.UnhandledException;
 import org.apache.log4j.Logger;
 import org.hibernate.Criteria;
+import org.hibernate.HibernateException;
 import org.hibernate.MappingException;
 import org.hibernate.Query;
 import org.hibernate.Session;
 import org.hibernate.SessionFactory;
+import org.hibernate.collection.internal.AbstractPersistentCollection;
+import org.hibernate.collection.spi.PersistentCollection;
+import org.hibernate.criterion.CriteriaSpecification;
+import org.hibernate.criterion.Criterion;
 import org.hibernate.criterion.Restrictions;
-import org.hibernate.impl.SessionFactoryImpl;
-import org.hibernate.impl.SessionImpl;
+import org.hibernate.engine.spi.CollectionEntry;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.internal.SessionFactoryImpl;
+import org.hibernate.internal.SessionImpl;
 import org.hibernate.metadata.ClassMetadata;
 import org.hibernate.metadata.CollectionMetadata;
 import org.hibernate.persister.collection.CollectionPersister;
@@ -47,18 +54,29 @@ import org.hibernate.type.CollectionType;
 import org.hibernate.type.ComponentType;
 import org.hibernate.type.DoubleType;
 import org.hibernate.type.EntityType;
+import org.hibernate.type.EnumType;
 import org.hibernate.type.FloatType;
 import org.hibernate.type.IntegerType;
 import org.hibernate.type.LongType;
+import org.hibernate.type.MaterializedClobType;
 import org.hibernate.type.SerializableType;
 import org.hibernate.type.SetType;
-import org.hibernate.type.StringClobType;
 import org.hibernate.type.StringType;
 import org.hibernate.type.Type;
-import org.joda.time.contrib.hibernate.PersistentDateTime;
+import org.jadira.usertype.dateandtime.joda.PersistentDateTime;
+import org.springframework.dao.DataAccessException;
 import org.springframework.stereotype.Repository;
 
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.DoubleResult;
+import eu.etaxonomy.cdm.datagenerator.FullCoverageDataGenerator;
+import eu.etaxonomy.cdm.hibernate.DOIUserType;
+import eu.etaxonomy.cdm.hibernate.EnumUserType;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
+import eu.etaxonomy.cdm.hibernate.PartialUserType;
+import eu.etaxonomy.cdm.hibernate.URIUserType;
+import eu.etaxonomy.cdm.hibernate.UUIDUserType;
+import eu.etaxonomy.cdm.hibernate.WSDLDefinitionUserType;
 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
 import eu.etaxonomy.cdm.model.common.Annotation;
 import eu.etaxonomy.cdm.model.common.CdmBase;
@@ -66,13 +84,19 @@ import eu.etaxonomy.cdm.model.common.Extension;
 import eu.etaxonomy.cdm.model.common.ICdmBase;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
 import eu.etaxonomy.cdm.model.common.Marker;
-import eu.etaxonomy.cdm.model.common.PartialUserType;
-import eu.etaxonomy.cdm.model.common.UUIDUserType;
-import eu.etaxonomy.cdm.model.common.WSDLDefinitionUserType;
+import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
 import eu.etaxonomy.cdm.model.name.BotanicalName;
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
+import eu.etaxonomy.cdm.strategy.match.CacheMatcher;
+import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy;
+import eu.etaxonomy.cdm.strategy.match.FieldMatcher;
+import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
+import eu.etaxonomy.cdm.strategy.match.IMatchable;
+import eu.etaxonomy.cdm.strategy.match.MatchException;
+import eu.etaxonomy.cdm.strategy.match.MatchMode;
+import eu.etaxonomy.cdm.strategy.match.Matching;
 import eu.etaxonomy.cdm.strategy.merge.DefaultMergeStrategy;
 import eu.etaxonomy.cdm.strategy.merge.IMergable;
 import eu.etaxonomy.cdm.strategy.merge.IMergeStrategy;
@@ -90,7 +114,6 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                String propertyName;
                Class<? extends CdmBase> otherClass;
                Class<? extends CdmBase> itemClass;
-               Field field;
                public boolean isCollection(){return itemClass != null;};
                public String toString(){return otherClass.getSimpleName() + "." + propertyName ;};
        }
@@ -100,20 +123,16 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                super(CdmBase.class);
        }
        
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getCdmBasesByFieldAndClass(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.common.CdmBase)
-        */
-       
+       @Override
        public List<CdmBase> getCdmBasesByFieldAndClass(Class clazz, String propertyName, CdmBase referencedCdmBase){
                Session session = super.getSession();
+       
                Criteria criteria = session.createCriteria(clazz);
                criteria.add(Restrictions.eq(propertyName, referencedCdmBase));
                return criteria.list();
        }
        
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getCdmBasesByFieldAndClass(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.common.CdmBase)
-        */
+       @Override
        public List<CdmBase> getCdmBasesWithItemInCollection(Class itemClass, Class clazz, String propertyName, CdmBase item){
                Session session = super.getSession();
                String thisClassStr = itemClass.getSimpleName();
@@ -125,9 +144,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                return result;
        }
        
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getAllCdmClasses(boolean)
-        */
+       @Override
        public Set<Class<? extends CdmBase>> getAllCdmClasses(boolean includeAbstractClasses){
                Set<Class<? extends CdmBase>> result = new HashSet<Class<? extends CdmBase>>();
                
@@ -155,9 +172,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                return result;
        }
        
-       
-
-       
+       @Override
        public Set<CdmBase> getReferencingObjects(CdmBase referencedCdmBase){
                Set<CdmBase> result = new HashSet<CdmBase>();
                try {
@@ -178,6 +193,28 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        throw new RuntimeException(e);
                }
                
+       }
+       @Override
+       public Set<CdmBase> getReferencingObjectsForDeletion(CdmBase referencedCdmBase){
+               Set<CdmBase> result = getReferencingObjects(referencedCdmBase);
+               Set<ReferenceHolder> holderSet = referenceMap.get(IdentifiableEntity.class);
+               try {
+                       if (holderSet == null){
+                               holderSet = makeHolderSet(IdentifiableEntity.class);
+                               referenceMap.put(IdentifiableEntity.class, holderSet);
+                       }
+                       Set<CdmBase> resultIdentifiableEntity = new HashSet<CdmBase>();
+                       for (ReferenceHolder refHolder: holderSet){
+                               handleReferenceHolder(referencedCdmBase, resultIdentifiableEntity, refHolder);
+                       }
+                       result.removeAll(resultIdentifiableEntity);
+                       
+                       return result;
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new RuntimeException(e);
+               }
+               
        }
 
        /**
@@ -202,7 +239,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
         * @throws NoSuchFieldException 
         * @throws ClassNotFoundException 
         */
-       private Set<ReferenceHolder> makeHolderSet(Class referencedClass) throws ClassNotFoundException, NoSuchFieldException {
+       private Set<ReferenceHolder> makeHolderSet(Class<?> referencedClass) throws ClassNotFoundException, NoSuchFieldException {
                Set<ReferenceHolder> result = new HashSet<ReferenceHolder>();
                
                //init
@@ -241,7 +278,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
        private void makePropertyType(
 //                     CdmBase referencedCdmBase,
                        Set<ReferenceHolder> result,
-                       Class referencedClass,
+                       Class<?> referencedClass,
                        SessionFactory sessionFactory, Class<? extends CdmBase> cdmClass,
                        Type propertyType, String propertyName, boolean isCollection)
                                throws ClassNotFoundException, NoSuchFieldException {
@@ -250,9 +287,9 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                if (propertyType.isEntityType()){
                        EntityType entityType = (EntityType)propertyType;
                        String associatedEntityName = entityType.getAssociatedEntityName();
-                       Class entityClass = Class.forName(associatedEntityName);
+                       Class<?> entityClass = Class.forName(associatedEntityName);
                        if (entityClass.isInterface()){
-                               System.out.println("So ein interface");
+                               logger.debug("There is an interface");
                        }
                        if (entityClass.isAssignableFrom(referencedClass)){
                                makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
@@ -260,14 +297,14 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                }else if (propertyType.isCollectionType()){
                        CollectionType collectionType = (CollectionType)propertyType;
                        //String role = collectionType.getRole();
-                       Type elType = collectionType.getElementType((SessionFactoryImpl)sessionFactory);
+                       Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
                        makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
                }else if (propertyType.isAnyType()){
-                       AnyType anyType = (AnyType)propertyType;
+//                     AnyType anyType = (AnyType)propertyType;
                        Field field = cdmClass.getDeclaredField(propertyName);
-                       Class returnType = field.getType();
+                       Class<?> returnType = field.getType();
                        if (returnType.isInterface()){
-                               System.out.println("So ein interface");
+                               logger.debug("There is an interface");
                        }
                        if (returnType.isAssignableFrom(referencedClass)){
                                makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
@@ -304,12 +341,12 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
 
        }
        
-       private boolean makeSingleProperty(Class itemClass, Class type, String propertyName, Class cdmClass, Set<ReferenceHolder> result,/*CdmBase item,*/ boolean isCollection){
+       private boolean makeSingleProperty(Class itemClass, Class<?> type, String propertyName, Class cdmClass, Set<ReferenceHolder> result,/*CdmBase item,*/ boolean isCollection){
                        String fieldName = StringUtils.rightPad(propertyName, 30);
                        String className = StringUtils.rightPad(cdmClass.getSimpleName(), 30);
                        String returnTypeName = StringUtils.rightPad(type.getSimpleName(), 30);
                        
-//                     System.out.println(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
+//                     logger.debug(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
                        ReferenceHolder refHolder = new ReferenceHolder();
                        refHolder.propertyName = propertyName;
                        refHolder.otherClass = cdmClass;
@@ -324,7 +361,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
         */
        private boolean isNoDoType(Type propertyType) {
                boolean result = false;
-               Class[] classes = new Class[]{
+               Class<?>[] classes = new Class[]{
                                PersistentDateTime.class, 
                                WSDLDefinitionUserType.class,
                                UUIDUserType.class, 
@@ -332,14 +369,18 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                                StringType.class,
                                BooleanType.class, 
                                IntegerType.class, 
-                               StringClobType.class,
+                               MaterializedClobType.class,
                                LongType.class,
                                FloatType.class,
                                SerializableType.class,
-                               DoubleType.class
+                               DoubleType.class,
+                               URIUserType.class,
+                               EnumType.class,
+                               EnumUserType.class,
+                               DOIUserType.class
                                };
                Set<String> classNames = new HashSet<String>();
-               for (Class clazz: classes){
+               for (Class<?> clazz: classes){
                        classNames.add(clazz.getCanonicalName());
                        if (clazz == propertyType.getClass()){
                                return true;
@@ -352,15 +393,20 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                return result;
        }
 
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getHqlResult(java.lang.String)
-        */
+       @Override
        public List<CdmBase> getHqlResult(String hqlQuery){
                Query query = getSession().createQuery(hqlQuery);
                List<CdmBase> result = query.list();
                return result;
        }
        
+       @Override
+       public Query getHqlQuery(String hqlQuery){
+               Query query = getSession().createQuery(hqlQuery);
+               return query;
+       }
+       
+       @Override
        public <T extends CdmBase> void   merge(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
                Class<T> clazz = (Class<T>)cdmBase1.getClass();
                SessionImpl session = (SessionImpl) getSession();
@@ -402,7 +448,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        //session.delete(null, mergable2, true, null);
                        session.delete(cdmBase2);
                        for (ICdmBase toBeDeleted : deleteSet){
-                               System.out.println("Delete " + toBeDeleted);
+                               logger.debug("Delete " + toBeDeleted);
                                if (toBeDeleted != cdmBase2){
                                        session.delete(toBeDeleted);
                                }
@@ -436,7 +482,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                
                SessionFactoryImpl sessionFactory = (SessionFactoryImpl) session.getSessionFactory();
                
-               Map allClassMetadata = sessionFactory.getAllClassMetadata();
+               Map<String, ClassMetadata> allClassMetadata = sessionFactory.getAllClassMetadata();
                
                //TODO cast
                getCollectionRoles(clazz, sessionFactory);
@@ -454,24 +500,25 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
-               Statistics statistics = sessionFactory.getStatistics();
-               System.out.println("");
+//             Statistics statistics = sessionFactory.getStatistics();
+               logger.debug("");
                ClassMetadata taxonMetaData = sessionFactory.getClassMetadata(Taxon.class);
                String ename = taxonMetaData.getEntityName();
                try {
                        Reference ref = sessionFactory.getReference();
-                       System.out.println("");
-               } catch (NamingException e) {
+                       logger.debug("");
+               } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
+
                //sessionFactory.get
                ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(clazz);
                Type[] propertyTypes = classMetadata.getPropertyTypes();
                int propertyNr = 0;
                for (Type propertyType: propertyTypes){
                        String propertyName = classMetadata.getPropertyNames()[propertyNr];
-                       System.out.println(propertyName);
+                       logger.debug(propertyName);
                        makeMergeProperty(cdmBase1, cdmBase2, propertyType, propertyName, sessionFactory, false);
                        propertyNr++;
                }
@@ -484,7 +531,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                                CollectionMetadata collMetadata2 = sessionFactory.getCollectionMetadata(collectionRole);
                                String role = collMetadata2.getRole();
                                Type elType = collMetadata2.getElementType();
-                               System.out.println(role);
+                               logger.debug(role);
                        }
                }
        }
@@ -505,7 +552,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        for (String collectionRole : collectionRoles){
                                CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(collectionRole);
                                CollectionPersister collPersister = sessionFactory.getCollectionPersister(collectionRole);
-                               System.out.println("");
+                               logger.debug("");
                        }
                }else{
                        logger.warn("Class metadata is not of type AbstractEntityPersister");
@@ -532,12 +579,12 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                                for (String collectionRole : collectionRoles){
                                        CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(collectionRole);
                                        String role = collMetadata.getRole();
-                                       System.out.println(role);
+                                       logger.debug(role);
                                        
                                }
                                
 //                             if (entityClass.isInterface()){
-//                                     System.out.println("So ein interface");
+//                                     logger.debug("So ein interface");
 //                             }
 //                             if (entityClass.isAssignableFrom(clazz)){
 //                                     makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
@@ -545,7 +592,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        }else if (propertyType.isCollectionType()){
                                CollectionType collectionType = (CollectionType)propertyType;
                                String role = collectionType.getRole();
-                               Type elType = collectionType.getElementType((SessionFactoryImpl)sessionFactory);
+                               Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
                                String n = collectionType.getAssociatedEntityName(sessionFactory);
                                CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(role);
                                if (collMetadata instanceof OneToManyPersister){
@@ -571,7 +618,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                                        }
                                        
                                }
-                               System.out.println("");
+                               logger.debug("");
                                
 //                     makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
                        }else if (propertyType.isAnyType()){
@@ -579,7 +626,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                                Field field = clazz.getDeclaredField(propertyName);
                                Class returnType = field.getType();
 //                     if (returnType.isInterface()){
-//                             System.out.println("So ein interface");
+//                             logger.debug("So ein interface");
 //                     }
 //                     if (returnType.isAssignableFrom(referencedClass)){
 //                             makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
@@ -727,7 +774,7 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                        query.setEntity("newValue", cdmBase1);
                        query.setInteger("id",referencingObject.getId());
                        int rowCount = query.executeUpdate();
-                       System.out.println("Rows affected: " + rowCount);
+                       logger.debug("Rows affected: " + rowCount);
                        session.refresh(referencingObject);
                }
            }
@@ -823,10 +870,8 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                }
        }
        
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#test()
-        */
-       public void test() {
+       //TODO Move to test classes if still needed
+       private void test() {
                SessionFactoryImpl factory = (SessionFactoryImpl)getSession().getSessionFactory();
                Type propType = factory.getReferencedPropertyType(BotanicalName.class.getCanonicalName(), "titleCache");
                Map collMetadata = factory.getAllCollectionMetadata();
@@ -840,18 +885,408 @@ public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdm
                }
                Statistics statistics = factory.getStatistics();
                Map allClassMetadata = factory.getAllClassMetadata();
-               System.out.println("");
+               logger.debug("");
                
        }
 
+       @Override
        public <T extends CdmBase> T find(Class<T> clazz, int id){
                Session session;
                session =  getSession();
                //session = getSession().getSessionFactory().getCurrentSession();
-               return (T)session.get(clazz, id);
+               Object o = session.get(clazz, id);
+               return (T)o;
+       }
+
+       @Override
+       public <T extends IMatchable> List<T> findMatching(T objectToMatch,
+                       IMatchStrategy matchStrategy) throws MatchException {
+               try {
+                       List<T> result = new ArrayList<T>();
+                       if(objectToMatch == null){
+                               return result;
+                       }
+                       if (matchStrategy == null){
+                               matchStrategy = DefaultMatchStrategy.NewInstance(objectToMatch.getClass());
+                       }
+                       result.addAll(findMatchingNullSafe(objectToMatch, matchStrategy));
+                       return result;
+               } catch (IllegalArgumentException e) {
+                       throw new MatchException(e);
+               } catch (IllegalAccessException e) {
+                       throw new MatchException(e);
+               }
+       }
+       
+       private <T extends IMatchable> List<T> findMatchingNullSafe(T objectToMatch,    IMatchStrategy matchStrategy) throws IllegalArgumentException, IllegalAccessException, MatchException {
+               List<T> result = new ArrayList<T>();
+               Session session = getSession();
+               Class<?> matchClass = objectToMatch.getClass();
+               ClassMetadata classMetaData = session.getSessionFactory().getClassMetadata(matchClass.getCanonicalName());
+               Criteria criteria = session.createCriteria(matchClass);
+               boolean noMatch = makeCriteria(objectToMatch, matchStrategy, classMetaData, criteria);
+               logger.debug(criteria);
+               //session.flush();
+               if (noMatch == false){
+                       List<T> matchCandidates = criteria.list();
+                       matchCandidates.remove(objectToMatch);
+                       for (T matchCandidate : matchCandidates ){
+                               if (matchStrategy.invoke(objectToMatch, matchCandidate)){
+                                       result.add(matchCandidate);
+                               }else{
+                                       logger.warn("Match candidate did not match: " + matchCandidate);
+                               }
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * @param <T>
+        * @param objectToMatch
+        * @param matchStrategy
+        * @param classMetaData
+        * @param criteria
+        * @return
+        * @throws IllegalAccessException
+        * @throws MatchException
+        */
+       private <T> boolean makeCriteria(T objectToMatch,
+                       IMatchStrategy matchStrategy, ClassMetadata classMetaData,
+                       Criteria criteria) throws IllegalAccessException, MatchException {
+               Matching matching = matchStrategy.getMatching();
+               boolean noMatch = false;
+               Map<String, List<MatchMode>> replaceMatchers = new HashMap<String, List<MatchMode>>();
+               for (CacheMatcher cacheMatcher: matching.getCacheMatchers()){
+                       boolean cacheProtected = (Boolean)cacheMatcher.getProtectedField(matching).get(objectToMatch);
+                       if (cacheProtected == true){
+                               String cacheValue = (String)cacheMatcher.getField().get(objectToMatch);
+                               if (StringUtils.isBlank(cacheValue)){
+                                       return true;  //no match
+                               }else{
+                                       criteria.add(Restrictions.eq(cacheMatcher.getPropertyName(), cacheValue));
+                                       criteria.add(Restrictions.eq(cacheMatcher.getProtectedPropertyName(), cacheProtected));
+                                       
+                                       List<DoubleResult<String, MatchMode>> replacementModes = cacheMatcher.getReplaceMatchModes(matching);
+                                       for (DoubleResult<String, MatchMode> replacementMode: replacementModes ){
+                                               String propertyName = replacementMode.getFirstResult();
+                                               List<MatchMode> replaceMatcherList = replaceMatchers.get(propertyName);
+                                               if (replaceMatcherList == null){
+                                                       replaceMatcherList = new ArrayList<MatchMode>();
+                                                       replaceMatchers.put(propertyName, replaceMatcherList);
+                                               }
+                                               replaceMatcherList.add(replacementMode.getSecondResult());
+                                       }
+
+                               }
+                       }
+               }
+               for (FieldMatcher fieldMatcher : matching.getFieldMatchers(false)){
+                       String propertyName = fieldMatcher.getPropertyName();
+                       Type propertyType = classMetaData.getPropertyType(propertyName);
+                       Object value = fieldMatcher.getField().get(objectToMatch);
+                       List<MatchMode> matchModes= new ArrayList<MatchMode>();
+                       matchModes.add(fieldMatcher.getMatchMode());
+                       if (replaceMatchers.get(propertyName) != null){
+                               matchModes.addAll(replaceMatchers.get(propertyName));
+                       }
+                       
+                       boolean isIgnore = false;
+                       for (MatchMode matchMode : matchModes){
+                               isIgnore |= matchMode.isIgnore(value);
+                       }
+                       if (! isIgnore ){
+                               if (propertyType.isComponentType()){
+                                       matchComponentType(criteria, fieldMatcher, propertyName, value, matchModes);
+                               }else{
+                                       noMatch = matchNonComponentType(criteria, fieldMatcher, propertyName, value, matchModes, propertyType);
+                               }
+                       }
+                       if (noMatch){
+                               return noMatch;
+                       }
+               }
+               return noMatch;
+       }
+
+       /**
+        * @param criteria
+        * @param fieldMatcher
+        * @param propertyName
+        * @param value
+        * @param matchMode
+        * @throws MatchException
+        * @throws IllegalAccessException
+        */
+       private void matchComponentType(Criteria criteria,
+                       FieldMatcher fieldMatcher, String propertyName, Object value,
+                       List<MatchMode> matchModes) throws MatchException, IllegalAccessException {
+               if (value == null){
+                       boolean requiresSecondNull = requiresSecondNull(matchModes, value);
+                       if (requiresSecondNull){
+                               criteria.add(Restrictions.isNull(propertyName));
+                       }else{
+                               //TODO 
+                               logger.warn("Component type not yet implemented for (null) value: " + propertyName);
+                               throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName);
+                       }
+               }else{
+                       Class<?> componentClass = fieldMatcher.getField().getType();
+                       Map<String, Field> fields = CdmUtils.getAllFields(componentClass, Object.class, false, false, true, false);
+                       for (String fieldName : fields.keySet()){
+                               String restrictionPath = propertyName +"."+fieldName;
+                               Object componentValue = fields.get(fieldName).get(value);
+                               //TODO diffentiate matchMode
+                               createCriterion(criteria, restrictionPath, componentValue, matchModes);
+                       }
+               }
+       }
+
+       private boolean matchNonComponentType(Criteria criteria,
+                       FieldMatcher fieldMatcher, String propertyName, Object value,
+                       List<MatchMode> matchModes, Type propertyType) throws HibernateException, DataAccessException, MatchException, IllegalAccessException{
+               boolean noMatch = false;
+               if (isRequired(matchModes) && value == null){
+                       noMatch = true;
+                       return noMatch;
+               }else if (requiresSecondNull(matchModes,value)){
+                       criteria.add(Restrictions.isNull(propertyName));
+               }else{
+                       if (isMatch(matchModes)){
+                               if (propertyType.isCollectionType()){
+                                       //TODO collection not yet handled for match     
+                               }else{
+                                       int joinType = CriteriaSpecification.INNER_JOIN;
+                                       if (! requiresSecondValue(matchModes,value)){
+                                               joinType = CriteriaSpecification.LEFT_JOIN;
+                                       }
+                                       Criteria matchCriteria = criteria.createCriteria(propertyName, joinType).add(Restrictions.isNotNull("id"));
+                                       Class matchClass = value.getClass();
+                                       if (IMatchable.class.isAssignableFrom(matchClass)){
+                                               IMatchStrategy valueMatchStrategy = DefaultMatchStrategy.NewInstance((Class<IMatchable>)matchClass);
+                                               ClassMetadata valueClassMetaData = getSession().getSessionFactory().getClassMetadata(matchClass.getCanonicalName());;
+                                               noMatch = makeCriteria(value, valueMatchStrategy, valueClassMetaData, matchCriteria); 
+                                       }else{
+                                               logger.error("Class to match (" + matchClass + ") is not of type IMatchable");
+                                               throw new MatchException("Class to match (" + matchClass + ") is not of type IMatchable");
+                                       }
+                               }
+                       }else if (isEqual(matchModes)){
+                               createCriterion(criteria, propertyName, value, matchModes);
+                       }else {
+                               logger.warn("Unhandled match mode: " + matchModes + ", value: " + (value==null?"null":value));
+                       }
+               }
+               return noMatch;
        }
        
+       /**
+        * @param criteria
+        * @param propertyName
+        * @param value
+        * @param matchMode
+        * @throws MatchException
+        */
+       private void createCriterion(Criteria criteria, String propertyName,
+                       Object value, List<MatchMode> matchModes) throws MatchException {
+               Criterion finalRestriction = null;
+               Criterion equalRestriction = Restrictions.eq(propertyName, value);
+               Criterion nullRestriction = Restrictions.isNull(propertyName);
+               if (this.requiresSecondValue(matchModes, value)){
+                       finalRestriction = equalRestriction;
+               }else if (requiresSecondNull(matchModes, value) ){
+                       finalRestriction = nullRestriction;
+               }else{
+                       finalRestriction = Restrictions.or(equalRestriction, nullRestriction);
+               }
+               //return finalRestriction;
+               criteria.add(finalRestriction);
+       }
        
+       /**
+        * @param matchModes
+        * @param value
+        * @return
+        * @throws MatchException 
+        */
+       private boolean requiresSecondNull(List<MatchMode> matchModes, Object value) throws MatchException {
+               boolean result = true;
+               for (MatchMode matchMode: matchModes){
+                       result &= matchMode.requiresSecondNull(value);
+               }
+               return result;
+       }
+       
+       /**
+        * @param matchModes
+        * @param value
+        * @return
+        * @throws MatchException 
+        */
+       private boolean requiresSecondValue(List<MatchMode> matchModes, Object value) throws MatchException {
+               boolean result = true;
+               for (MatchMode matchMode: matchModes){
+                       result &= matchMode.requiresSecondValue(value);
+               }
+               return result;
+       }
+       
+       /**
+        * @param matchModes
+        * @param value
+        * @return
+        * @throws MatchException 
+        */
+       private boolean isRequired(List<MatchMode> matchModes) throws MatchException {
+               boolean result = true;
+               for (MatchMode matchMode: matchModes){
+                       result &= matchMode.isRequired();
+               }
+               return result;
+       }
+       
+       /**
+        * Returns true if at least one match mode is of typ MATCH_XXX
+        * @param matchModes
+        * @param value
+        * @return
+        * @throws MatchException 
+        */
+       private boolean isMatch(List<MatchMode> matchModes) throws MatchException {
+               boolean result = false;
+               for (MatchMode matchMode: matchModes){
+                       result |= matchMode.isMatch();
+               }
+               return result;
+       }
+
+       /**
+        * Returns true if at least one match mode is of typ EQUAL_XXX
+        * @param matchModes
+        * @param value
+        * @return
+        * @throws MatchException 
+        */
+       private boolean isEqual(List<MatchMode> matchModes) throws MatchException {
+               boolean result = false;
+               for (MatchMode matchMode: matchModes){
+                       result |= matchMode.isEqual();
+               }
+               return result;
+       }
+
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#saveMetaData(eu.etaxonomy.cdm.model.common.CdmMetaData)
+        */
+       public void saveMetaData(CdmMetaData cdmMetaData) {
+               getSession().saveOrUpdate(cdmMetaData);
+       }
+
+       /* (non-Javadoc)
+        * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getMetaData()
+        */
+       public List<CdmMetaData> getMetaData() {
+               Session session = getSession();
+               Criteria crit = session.createCriteria(CdmMetaData.class);
+               List<CdmMetaData> results = crit.list();
+               return results;
+       }
+       
+    @Override
+    public PersistentCollection initializeCollection(PersistentCollection col) {
+            Session session = getSession();
+            col.setCurrentSession((SessionImplementor) session);
+            
+            if(!((SessionImplementor)session).getPersistenceContext().getCollectionEntries().containsKey(col)) {
+                    ((SessionImplementor)session).getPersistenceContext().addUninitializedDetachedCollection(
+                                    ((SessionImplementor)session).getFactory().getCollectionPersister( col.getRole() ),col);
+            }
+            col.forceInitialization();    
+            logger.debug("initialising persistent collection with with role : " + col.getRole() + " and key : " + col.getKey());
+            return col;
+    }
+    
+    @Override
+    public boolean isEmpty(PersistentCollection col) {
+       return initializeCollection(col).empty();
+    }
+    
+    @Override
+    public int size(PersistentCollection col) {
+       logger.debug("remote size() - for role : " + col.getRole() + " and key : " + col.getKey());
+       initializeCollection(col);   
+       SessionImplementor session = (SessionImplementor)getSession();
+       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(col);
+
+       if ( entry != null ) {
+               
+               CollectionPersister persister = entry.getLoadedPersister();                     
+               return persister.getSize( entry.getLoadedKey(), session );
+       }
+       return -1;
+    }
+    
+    @Override
+    public Object get(PersistentCollection col, int index) {
+       logger.debug("remote get() - for role : " + col.getRole() + " and key : " + col.getKey());
+       initializeCollection(col);    
+       SessionImplementor session = (SessionImplementor)getSession();
+       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(col);
+       
+       if ( entry != null ) {
+               
+               CollectionPersister persister = entry.getLoadedPersister();                             
+               return persister.getElementByIndex(entry.getLoadedKey(), index, session, col);
+               
+       }
+       //FIXME:Remoting Should we not be throwing an exception here ?
+       return null;
+    }
+    
+    @Override
+    public boolean contains(PersistentCollection col, Object element) {
+       logger.debug("remote contains() - for role : " + col.getRole() + " and key : " + col.getKey());
+       initializeCollection(col);    
+       SessionImplementor session = (SessionImplementor)getSession();
+       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(col);
+       
+       if ( entry != null ) {
+               
+               CollectionPersister persister = entry.getLoadedPersister();             
+               return persister.elementExists(entry.getLoadedKey(), element, session);                                 
+       }
+       //FIXME:Remoting Should we not be throwing an exception here ?
+       return false;
+    }
+
+    @Override
+    public boolean containsKey(PersistentCollection col, Object key) {
+       logger.debug("remote containsKey() - for role : " + col.getRole() + " and key : " + col.getKey());
+       initializeCollection(col);    
+       SessionImplementor session = (SessionImplementor)getSession();
+       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(col);
+       
+       if ( entry != null ) {
+               
+               CollectionPersister persister = entry.getLoadedPersister();             
+               return persister.indexExists(entry.getLoadedKey(), key, session);                               
+       }
+       //FIXME:Remoting Should we not be throwing an exception here ?
+       return false;
+       
+    }
+    
+    @Override
+    public boolean containsValue(PersistentCollection col, Object element) {           
+       return contains(col, element);
+    }
+
+       @Override
+       public void createFullSampleData() {
+               FullCoverageDataGenerator dataGenerator = new FullCoverageDataGenerator();
+               dataGenerator.fillWithData(getSession());
+       }
+
 }