allow saving FullDataGenerator data through API (preliminary)
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / CdmGenericDaoImpl.java
index 91ba458b88c4012c0c79829828f478dca1d4e09d..a4dcdf05a52b9f79bc4ccbfed393982c6eaaef6b 100644 (file)
 
 package eu.etaxonomy.cdm.persistence.dao.hibernate.common;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.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;
+import org.hibernate.persister.collection.OneToManyPersister;
+import org.hibernate.persister.entity.AbstractEntityPersister;
+import org.hibernate.stat.Statistics;
+import org.hibernate.type.AnyType;
+import org.hibernate.type.BooleanType;
+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.StringType;
+import org.hibernate.type.Type;
+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;
+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.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;
+import eu.etaxonomy.cdm.strategy.merge.MergeException;
 
-//@Repository
-@Repository(value="cdmDao")
+@Repository
 public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdmGenericDao{
+       private static final Logger logger = Logger.getLogger(CdmGenericDaoImpl.class);
 
+       
+       private Set<Class<? extends CdmBase>> allCdmClasses = null;
+       private Map<Class<? extends CdmBase>, Set<ReferenceHolder>> referenceMap = new HashMap<Class<? extends CdmBase>, Set<ReferenceHolder>>();
+       
+       private class ReferenceHolder{
+               String propertyName;
+               Class<? extends CdmBase> otherClass;
+               Class<? extends CdmBase> itemClass;
+               public boolean isCollection(){return itemClass != null;};
+               public String toString(){return otherClass.getSimpleName() + "." + propertyName ;};
+       }
+
+       
        public CdmGenericDaoImpl() {
                super(CdmBase.class);
        }
        
+       @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();
+       }
+       
+       @Override
+       public List<CdmBase> getCdmBasesWithItemInCollection(Class itemClass, Class clazz, String propertyName, CdmBase item){
+               Session session = super.getSession();
+               String thisClassStr = itemClass.getSimpleName();
+               String otherClassStr = clazz.getSimpleName();
+               String queryStr = " SELECT other FROM "+ thisClassStr + " this, " + otherClassStr + " other " + 
+                       " WHERE this = :referencedObject AND this member of other."+propertyName ;
+               Query query = session.createQuery(queryStr).setEntity("referencedObject", item);
+               List<CdmBase> result = query.list();
+               return result;
+       }
+       
+       @Override
+       public Set<Class<? extends CdmBase>> getAllCdmClasses(boolean includeAbstractClasses){
+               Set<Class<? extends CdmBase>> result = new HashSet<Class<? extends CdmBase>>();
+               
+               SessionFactory sessionFactory = getSession().getSessionFactory();
+               Map<?,?> allClassMetadata = sessionFactory.getAllClassMetadata();
+               Collection<?> keys = allClassMetadata.keySet();
+               for (Object oKey : keys){
+                       if (oKey instanceof String){
+                               String strKey = (String)oKey;
+                               if (! strKey.endsWith("_AUD")){
+                                       try {
+                                               Class clazz = Class.forName(strKey);
+                                               boolean isAbstractClass = Modifier.isAbstract(clazz.getModifiers());
+                                               if (! isAbstractClass || includeAbstractClasses){
+                                                       result.add(clazz);
+                                               }
+                                       } catch (ClassNotFoundException e) {
+                                               logger.warn("Class not found: " + strKey);
+                                       }
+                               }
+                       }else{
+                               logger.warn("key is not of type String: " +  oKey);
+                       }
+               }
+               return result;
+       }
+       
+       @Override
+       public Set<CdmBase> getReferencingObjects(CdmBase referencedCdmBase){
+               Set<CdmBase> result = new HashSet<CdmBase>();
+               try {
+                       referencedCdmBase = (CdmBase)HibernateProxyHelper.deproxy(referencedCdmBase);
+                       Class<? extends CdmBase> referencedClass = referencedCdmBase.getClass();
+                       
+                       Set<ReferenceHolder> holderSet = referenceMap.get(referencedClass);
+                       if (holderSet == null){
+                               holderSet = makeHolderSet(referencedClass);
+                               referenceMap.put(referencedClass, holderSet);
+                       }
+                       for (ReferenceHolder refHolder: holderSet){
+                               handleReferenceHolder(referencedCdmBase, result, refHolder);
+                       }
+                       return result;  
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       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);
+               }
+               
+       }
+
+       /**
+        * @param referencedCdmBase
+        * @param result
+        * @param refHolder
+        */
+       private void handleReferenceHolder(CdmBase referencedCdmBase,
+                       Set<CdmBase> result, ReferenceHolder refHolder) {
+               boolean isCollection = refHolder.isCollection();
+               if (isCollection){
+                       result.addAll(getCdmBasesWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase));
+               }else{
+                       result.addAll(getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, referencedCdmBase));
+               }
+       }
+
+       
+       /**
+        * @param referencedClass
+        * @return
+        * @throws NoSuchFieldException 
+        * @throws ClassNotFoundException 
+        */
+       private Set<ReferenceHolder> makeHolderSet(Class<?> referencedClass) throws ClassNotFoundException, NoSuchFieldException {
+               Set<ReferenceHolder> result = new HashSet<ReferenceHolder>();
+               
+               //init
+               if (allCdmClasses == null){
+                       allCdmClasses = getAllCdmClasses(false); //findAllCdmClasses();
+               }
+               //referencedCdmBase = (CdmBase)HibernateProxyHelper.deproxy(referencedCdmBase);
+               SessionFactory sessionFactory = getSession().getSessionFactory();
+               
+               
+               for (Class<? extends CdmBase> cdmClass : allCdmClasses){
+                       ClassMetadata classMetadata = sessionFactory.getClassMetadata(cdmClass);
+                       Type[] propertyTypes = classMetadata.getPropertyTypes();
+                       int propertyNr = 0;
+                       for (Type propertyType: propertyTypes){
+                               String propertyName = classMetadata.getPropertyNames()[propertyNr];
+                               makePropertyType(result, referencedClass, sessionFactory, cdmClass, propertyType, propertyName, false);
+                               propertyNr++;
+                       }
+                       
+               }
+               return result;
+       }
+
+       /**
+        * @param referencedCdmBase
+        * @param result
+        * @param referencedClass
+        * @param sessionFactory
+        * @param cdmClass
+        * @param propertyType
+        * @param propertyName
+        * @throws ClassNotFoundException
+        * @throws NoSuchFieldException
+        */
+       private void makePropertyType(
+//                     CdmBase referencedCdmBase,
+                       Set<ReferenceHolder> result,
+                       Class<?> referencedClass,
+                       SessionFactory sessionFactory, Class<? extends CdmBase> cdmClass,
+                       Type propertyType, String propertyName, boolean isCollection)
+                               throws ClassNotFoundException, NoSuchFieldException {
+               
+               
+               if (propertyType.isEntityType()){
+                       EntityType entityType = (EntityType)propertyType;
+                       String associatedEntityName = entityType.getAssociatedEntityName();
+                       Class<?> entityClass = Class.forName(associatedEntityName);
+                       if (entityClass.isInterface()){
+                               logger.debug("There is an interface");
+                       }
+                       if (entityClass.isAssignableFrom(referencedClass)){
+                               makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
+                       }
+               }else if (propertyType.isCollectionType()){
+                       CollectionType collectionType = (CollectionType)propertyType;
+                       //String role = collectionType.getRole();
+                       Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
+                       makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
+               }else if (propertyType.isAnyType()){
+//                     AnyType anyType = (AnyType)propertyType;
+                       Field field = cdmClass.getDeclaredField(propertyName);
+                       Class<?> returnType = field.getType();
+                       if (returnType.isInterface()){
+                               logger.debug("There is an interface");
+                       }
+                       if (returnType.isAssignableFrom(referencedClass)){
+                               makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
+                       }
+               }else if (propertyType.isComponentType()){
+                       ComponentType componentType = (ComponentType)propertyType;
+                       Type[] subTypes = componentType.getSubtypes();
+//                     Field field = cdmClass.getDeclaredField(propertyName);
+//                     Class returnType = field.getType();
+                       int propertyNr = 0;
+                       for (Type subType: subTypes){
+                               String subPropertyName = componentType.getPropertyNames()[propertyNr];
+                               if (!isNoDoType(subType)){
+                                       logger.warn("SubType not yet handled: " + subType);
+                               }
+//                             handlePropertyType(referencedCdmBase, result, referencedClass, 
+//                                             sessionFactory, cdmClass, subType, subPropertyName, isCollection);
+                               propertyNr++;
+                       }
+               }else if (isNoDoType(propertyType)){
+                       //do nothing
+               }else{
+                       logger.warn("propertyType not yet handled: " + propertyType.getName());
+               }
+               //OLD: 
+                               //              if (! type.isInterface()){
+               //              if (referencedClass.isAssignableFrom(type)|| 
+               //                              type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
+               //                      handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
+               //              }
+               //      //interface
+               //      }else if (type.isAssignableFrom(referencedClass)){
+               //                      handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
+
+       }
+       
+       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);
+                       
+//                     logger.debug(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
+                       ReferenceHolder refHolder = new ReferenceHolder();
+                       refHolder.propertyName = propertyName;
+                       refHolder.otherClass = cdmClass;
+                       refHolder.itemClass = (isCollection ? itemClass : null) ;
+                       result.add(refHolder);
+               return true;
+       }
+
+       /**
+        * @param propertyType
+        * @return
+        */
+       private boolean isNoDoType(Type propertyType) {
+               boolean result = false;
+               Class<?>[] classes = new Class[]{
+                               PersistentDateTime.class, 
+                               WSDLDefinitionUserType.class,
+                               UUIDUserType.class, 
+                               PartialUserType.class,
+                               StringType.class,
+                               BooleanType.class, 
+                               IntegerType.class, 
+                               MaterializedClobType.class,
+                               LongType.class,
+                               FloatType.class,
+                               SerializableType.class,
+                               DoubleType.class,
+                               URIUserType.class,
+                               EnumType.class,
+                               EnumUserType.class,
+                               DOIUserType.class
+                               };
+               Set<String> classNames = new HashSet<String>();
+               for (Class<?> clazz: classes){
+                       classNames.add(clazz.getCanonicalName());
+                       if (clazz == propertyType.getClass()){
+                               return true;
+                       }
+               }
+               String propertyTypeClassName = propertyType.getName();
+               if (classNames.contains(propertyTypeClassName)){
+                       return true;
+               }
+               return result;
+       }
+
+       @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();
+               SessionFactory sessionFactory = session.getSessionFactory();
+               if (mergeStrategy == null){
+                       mergeStrategy = DefaultMergeStrategy.NewInstance(cdmBase1.getClass());
+               }
+               try {
+                       //test null and types
+                       testMergeValid(cdmBase1, cdmBase2);
+                       
+                       //merge objects
+                       //externel impl
+                       //internal impl
+                       session.flush();
+                       Set<ICdmBase> deleteSet = new HashSet<ICdmBase>();
+                       Set<ICdmBase> cloneSet = new HashSet<ICdmBase>();
+                       if (cdmBase1 instanceof IMergable){
+                               IMergable mergable1 = (IMergable)cdmBase1;
+                               IMergable mergable2 = (IMergable)cdmBase2;
+                               deleteSet = mergeStrategy.invoke(mergable1, mergable2, cloneSet);
+                               //session.saveOrUpdate(mergable1);
+
+                               session.flush();
+                               //((IMergable)cdmBase1).mergeInto(cdmBase2, DefaultMergeStrategy.NewInstance(cdmBase1.getClass()));
+                       }else{
+                               mergeExternal(cdmBase1, cdmBase2, clazz, session);
+                       }
+                       
+                       
+                       if (cdmBase2.getId() > 0){
+                               session.saveOrUpdate(cdmBase2);
+                               //rearrange references to cdmBase2
+                               reallocateReferences(cdmBase1, cdmBase2, sessionFactory, clazz, cloneSet);
+                       }
+                       
+                       //remove deleted objects 
+                       
+                       //session.delete(null, mergable2, true, null);
+                       session.delete(cdmBase2);
+                       for (ICdmBase toBeDeleted : deleteSet){
+                               logger.debug("Delete " + toBeDeleted);
+                               if (toBeDeleted != cdmBase2){
+                                       session.delete(toBeDeleted);
+                               }
+                               
+                       }
+                       
+                       //flush
+                       session.flush();
+                       
+               } catch (Exception e) {
+                       throw new MergeException(e);
+               } 
+       }
+
+       
+       /**
+        * @param <T>
+        * @param cdmBase1
+        * @param cdmBase2
+        * @param clazz
+        * @param sessionFactory
+        * @throws MergeException 
+        * @throws ClassNotFoundException
+        * @throws NoSuchFieldException
+        */
+       private <T extends CdmBase> void mergeExternal(T cdmBase1, T cdmBase2, Class<T> clazz,
+                       Session session) throws MergeException {
+//             handleAnnotations
+               logger.warn("Merge external");
+               handleAnnotationsEtc(cdmBase1, cdmBase2, session);
+               
+               SessionFactoryImpl sessionFactory = (SessionFactoryImpl) session.getSessionFactory();
+               
+               Map<String, ClassMetadata> allClassMetadata = sessionFactory.getAllClassMetadata();
+               
+               //TODO cast
+               getCollectionRoles(clazz, sessionFactory);
+               
+               TaxonNameBase name1 = BotanicalName.NewInstance(null);
+               name1.getTaxonBases();
+               
+               Type propType = sessionFactory.getReferencedPropertyType(BotanicalName.class.getCanonicalName(), "taxonBases");
+               Map collMetadata = sessionFactory.getAllCollectionMetadata();
+               //roles = sessionFactory.getCollectionRolesByEntityParticipant("eu.etaxonomy.cdm.model.name.BotanicalName");
+               CollectionPersister collPersister;
+               try {
+                       collPersister = sessionFactory.getCollectionPersister(TaxonNameBase.class.getCanonicalName()+".annotations");
+               } catch (MappingException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+//             Statistics statistics = sessionFactory.getStatistics();
+               logger.debug("");
+               ClassMetadata taxonMetaData = sessionFactory.getClassMetadata(Taxon.class);
+               String ename = taxonMetaData.getEntityName();
+               try {
+                       Reference ref = sessionFactory.getReference();
+                       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];
+                       logger.debug(propertyName);
+                       makeMergeProperty(cdmBase1, cdmBase2, propertyType, propertyName, sessionFactory, false);
+                       propertyNr++;
+               }
+               Set<String> collectionRoles;
+               if (classMetadata instanceof AbstractEntityPersister){
+                       AbstractEntityPersister persister = (AbstractEntityPersister)classMetadata;
+                       String rootName = persister.getRootEntityName();
+                       collectionRoles = sessionFactory.getCollectionRolesByEntityParticipant(rootName);
+                       for (String collectionRole : collectionRoles){
+                               CollectionMetadata collMetadata2 = sessionFactory.getCollectionMetadata(collectionRole);
+                               String role = collMetadata2.getRole();
+                               Type elType = collMetadata2.getElementType();
+                               logger.debug(role);
+                       }
+               }
+       }
+
+       /**
+        * @param <T>
+        * @param clazz
+        * @param sessionFactory
+        */
+       private <T> Set<String> getCollectionRoles(Class<T> clazz,
+                       SessionFactoryImpl sessionFactory) {
+               Set<String> collectionRoles = null;
+               ClassMetadata classMetaData = sessionFactory.getClassMetadata(clazz);
+               if (classMetaData instanceof AbstractEntityPersister){
+                       AbstractEntityPersister persister = (AbstractEntityPersister)classMetaData;
+                       String rootName = persister.getRootEntityName();
+                       collectionRoles = sessionFactory.getCollectionRolesByEntityParticipant(rootName);
+                       for (String collectionRole : collectionRoles){
+                               CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(collectionRole);
+                               CollectionPersister collPersister = sessionFactory.getCollectionPersister(collectionRole);
+                               logger.debug("");
+                       }
+               }else{
+                       logger.warn("Class metadata is not of type AbstractEntityPersister");
+                       throw new UnhandledException("Class metadata is not of type AbstractEntityPersister", null);
+               }
+               return collectionRoles;
+       }
+       
+       
+       private <T extends CdmBase> void makeMergeProperty(T cdmBase1, T cdmBase2, Type propertyType, String propertyName, SessionFactoryImpl sessionFactory, boolean isCollection) throws MergeException
+                        {
+       
+               try {
+                       Class<T> clazz = (Class<T>)cdmBase1.getClass();
+                       if (isNoDoType(propertyType)){
+                                               //do nothing 
+                       }else if (propertyType.isEntityType()){
+                               //Field field = clazz.getField(propertyName);   
+                               EntityType entityType = (EntityType)propertyType;
+                               String associatedEntityName = entityType.getAssociatedEntityName();
+                               Class entityClass = Class.forName(associatedEntityName);
+//                              Type refPropType = sessionFactory.getReferencedPropertyType(entityClass.getCanonicalName(), propertyName);
+                               Set<String> collectionRoles = getCollectionRoles(clazz, sessionFactory);
+                               for (String collectionRole : collectionRoles){
+                                       CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(collectionRole);
+                                       String role = collMetadata.getRole();
+                                       logger.debug(role);
+                                       
+                               }
+                               
+//                             if (entityClass.isInterface()){
+//                                     logger.debug("So ein interface");
+//                             }
+//                             if (entityClass.isAssignableFrom(clazz)){
+//                                     makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
+//                             }
+                       }else if (propertyType.isCollectionType()){
+                               CollectionType collectionType = (CollectionType)propertyType;
+                               String role = collectionType.getRole();
+                               Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
+                               String n = collectionType.getAssociatedEntityName(sessionFactory);
+                               CollectionMetadata collMetadata = sessionFactory.getCollectionMetadata(role);
+                               if (collMetadata instanceof OneToManyPersister){
+                                       OneToManyPersister oneManyPersister = (OneToManyPersister)collMetadata;
+                                       String className = oneManyPersister.getOwnerEntityName();
+                                       Class<?> myClass = Class.forName(className);
+                                       Field field = myClass.getDeclaredField(propertyName);
+                                       field.setAccessible(true);
+                                       try {
+                                               if (collectionType instanceof SetType){
+                                                       Set set2 = (Set)field.get(cdmBase2);
+                                                       Set<Object> set1 = (Set<Object>)field.get(cdmBase1);
+                                                       for (Object obj2: set2){
+                                                               set1.add(obj2);
+                                                       }
+                                               }
+                                       } catch (IllegalArgumentException e) {
+                                               // TODO Auto-generated catch block
+                                               e.printStackTrace();
+                                       } catch (IllegalAccessException e) {
+                                               // TODO Auto-generated catch block
+                                               e.printStackTrace();
+                                       }
+                                       
+                               }
+                               logger.debug("");
+                               
+//                     makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
+                       }else if (propertyType.isAnyType()){
+                               AnyType anyType = (AnyType)propertyType;
+                               Field field = clazz.getDeclaredField(propertyName);
+                               Class returnType = field.getType();
+//                     if (returnType.isInterface()){
+//                             logger.debug("So ein interface");
+//                     }
+//                     if (returnType.isAssignableFrom(referencedClass)){
+//                             makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
+//                     }
+                       }else if (propertyType.isComponentType()){
+                               ComponentType componentType = (ComponentType)propertyType;
+                               Type[] subTypes = componentType.getSubtypes();
+//             Field field = cdmClass.getDeclaredField(propertyName);
+//             Class returnType = field.getType();
+                               int propertyNr = 0;
+                               for (Type subType: subTypes){
+                                       String subPropertyName = componentType.getPropertyNames()[propertyNr];
+                                       if (!isNoDoType(subType)){
+                                               logger.warn("SubType not yet handled: " + subType);
+                                       }
+//                                     handlePropertyType(referencedCdmBase, result, referencedClass, 
+//                                     sessionFactory, cdmClass, subType, subPropertyName, isCollection);
+                                       propertyNr++;
+                               }
+                       }else{
+                               logger.warn("propertyType not yet handled: " + propertyType.getName());
+                       }
+                       //OLD: 
+                                       //              if (! type.isInterface()){
+                       //              if (referencedClass.isAssignableFrom(type)|| 
+                       //                              type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
+                       //                      handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
+                       //              }
+                       //      //interface
+                       //      }else if (type.isAssignableFrom(referencedClass)){
+                       //                      handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
+               } catch (Exception e) {
+                       throw new MergeException(e);
+               }
+       }
+
+
+               
+               
+       private void reallocateReferences(CdmBase cdmBase1, CdmBase cdmBase2, SessionFactory sessionFactory, Class clazz, Set<ICdmBase> cloneSet){
+               try {
+                       Set<ReferenceHolder> holderSet = referenceMap.get(clazz);
+                       if (holderSet == null){
+                               holderSet = makeHolderSet(clazz);
+                               referenceMap.put(clazz, holderSet);
+                       }
+                       for (ReferenceHolder refHolder: holderSet){
+                               reallocateByHolder(cdmBase1, cdmBase2, refHolder, cloneSet);
+                       }
+                       return; 
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new RuntimeException(e);
+               }
+       }
+       
+       /**
+        * @param cdmBase1
+        * @param cdmBase2
+        * @param refHolder
+        * @throws MergeException 
+        */
+       private void reallocateByHolder(CdmBase cdmBase1, CdmBase cdmBase2, ReferenceHolder refHolder, Set<ICdmBase> cloneSet) throws MergeException {
+               try {
+                       if (refHolder.isCollection()){
+                               reallocateCollection(cdmBase1, cdmBase2, refHolder, cloneSet);
+                       }else{
+                               reallocateSingleItem(cdmBase1, cdmBase2, refHolder, cloneSet);
+                       }
+               } catch (Exception e) {
+                       throw new MergeException("Error during reallocation of references to merge object: " + cdmBase2, e);
+               }
+               
+       }
+
+       /**
+        * @param cdmBase1
+        * @param cdmBase2
+        * @param refHolder
+        * @param cloneSet
+        * @throws MergeException 
+        * @throws NoSuchFieldException 
+        * @throws SecurityException 
+        * @throws IllegalAccessException 
+        * @throws IllegalArgumentException 
+        * @throws InvocationTargetException 
+        */
+       private void reallocateCollection(CdmBase cdmBase1, CdmBase cdmBase2,
+                       ReferenceHolder refHolder, Set<ICdmBase> cloneSet) throws MergeException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+               List<CdmBase> list = getCdmBasesWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, cdmBase2);
+               for (CdmBase referencingObject : list){
+                       Field referencingField = getFieldRecursive(refHolder.otherClass, refHolder.propertyName);
+                       referencingField.setAccessible(true);
+                       Object collection = referencingField.get(referencingObject);
+                       if (! (collection instanceof Collection)){
+                               throw new MergeException ("Reallocation of collections for collection other than set and list not yet implemented");
+                       }
+                       Method addMethod = DefaultMergeStrategy.getAddMethod(referencingField, false);
+                       Method removeMethod = DefaultMergeStrategy.getAddMethod(referencingField, true);
+                       addMethod.invoke(referencingObject, cdmBase1);
+                       removeMethod.invoke(referencingObject, cdmBase2);
+               }
+       }
+       
+       private Field getFieldRecursive(Class clazz, String propertyName) throws NoSuchFieldException{
+               try {
+                       return clazz.getDeclaredField(propertyName);
+               } catch (NoSuchFieldException e) {
+                       Class superClass = clazz.getSuperclass();
+                       if (CdmBase.class.isAssignableFrom(superClass)){
+                               return getFieldRecursive(superClass, propertyName);
+                       }else{
+                               throw e;
+                       }
+               }
+       }
+
+       /**
+        * @throws NoSuchFieldException 
+        * @throws SecurityException 
+        * @throws IllegalAccessException 
+        * @throws IllegalArgumentException 
+        * 
+        */
+       private void reallocateSingleItem_Old(CdmBase cdmBase1, CdmBase cdmBase2, ReferenceHolder refHolder) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+               List<CdmBase> referencingObjects = getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, cdmBase2);
+               for (CdmBase referencingObject : referencingObjects){
+                       Field referencingField = refHolder.otherClass.getDeclaredField(refHolder.propertyName);
+                       referencingField.setAccessible(true);
+                       Object test = referencingField.get(referencingObject);
+                       assert(test.equals(cdmBase2));
+                       referencingField.set(referencingObject, cdmBase1);
+               }
+       }
+
+       private void reallocateSingleItem(CdmBase cdmBase1, CdmBase cdmBase2, ReferenceHolder refHolder, Set<ICdmBase> cloneSet) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+               List<CdmBase> referencingObjects = getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, cdmBase2);
+               Session session = getSession(); 
+               for (CdmBase referencingObject : referencingObjects){
+                       if (!cloneSet.contains(referencingObject)){
+                       String className = refHolder.otherClass.getSimpleName();
+                   String propertyName = refHolder.propertyName;
+                       String hql = "update " + className + " c set c."+propertyName+" = :newValue where c.id = :id";
+                       Query query = session.createQuery(hql);
+                       query.setEntity("newValue", cdmBase1);
+                       query.setInteger("id",referencingObject.getId());
+                       int rowCount = query.executeUpdate();
+                       logger.debug("Rows affected: " + rowCount);
+                       session.refresh(referencingObject);
+               }
+           }
+               session.flush();
+       }
+       
+
+
+       /**
+        * @param <T>
+        * @param cdmBase1
+        * @param cdmBase2
+        * @param session
+        */
+       private <T> void handleAnnotationsEtc(T cdmBase1, T cdmBase2, Session session) {
+               //when handling annotations and other elements linked via @Any an JDBC errors occurs
+               //due to the unique column constraint in the association table on the column referencing
+               //the annotation.
+               //For some reason not delete command is executed for the old collection
+               // Hibernate bug ??
+               session.flush();  //for debugging
+               if (cdmBase1 instanceof AnnotatableEntity){
+                       AnnotatableEntity annotatableEntity1 = (AnnotatableEntity)cdmBase1;
+                       AnnotatableEntity annotatableEntity2 = (AnnotatableEntity)cdmBase2;
+                       //annotations
+                       List<Annotation> removeListAnnotation = new ArrayList<Annotation>();
+                       for (Annotation annotation : annotatableEntity2.getAnnotations()){
+                               Annotation clone = null;
+                               try {
+                                       clone = annotation.clone(annotatableEntity1);
+                               } catch (CloneNotSupportedException e) {
+                                       e.printStackTrace();
+                               }
+                               annotatableEntity1.addAnnotation(clone);
+                               removeListAnnotation.add(annotation);
+                       }
+                       for (Annotation annotation : removeListAnnotation){
+                               annotatableEntity2.removeAnnotation(annotation);
+                               getSession().delete(annotation);
+                       }
+                       //marker
+                       List<Marker> removeListMarker = new ArrayList<Marker>();
+                       for (Marker marker : annotatableEntity2.getMarkers()){
+                               Marker clone = null;
+                               try {
+                                       clone = marker.clone(annotatableEntity1);
+                               } catch (CloneNotSupportedException e) {
+                                       e.printStackTrace();
+                               }
+                               annotatableEntity1.addMarker(clone);
+                               removeListMarker.add(marker);
+                       }
+                       for (Marker marker : removeListMarker){
+                               annotatableEntity2.removeMarker(marker);
+                               getSession().delete(marker);
+                       }
+               }
+               if (cdmBase1 instanceof IdentifiableEntity){
+                       IdentifiableEntity identifiableEntity1 = (IdentifiableEntity)cdmBase1;
+                       IdentifiableEntity identifiableEntity2 = (IdentifiableEntity)cdmBase2;
+                       //annotations
+                       List<Extension> removeListExtension = new ArrayList<Extension>();
+                       for (Extension changeObject : (Set<Extension>)identifiableEntity2.getExtensions()){
+                               try {
+                                       Extension clone = changeObject.clone(identifiableEntity1);
+                                       identifiableEntity1.addExtension(clone);
+                                       removeListExtension.add(changeObject);
+                               } catch (CloneNotSupportedException e) {
+                                       e.printStackTrace();
+                               }
+                               
+                       }
+                       for (Extension removeObject : removeListExtension){
+                               identifiableEntity2.removeExtension(removeObject);
+                               getSession().delete(removeObject);
+                       }
+               }
+               
+               session.saveOrUpdate(cdmBase1);
+               session.saveOrUpdate(cdmBase2);
+               session.flush();
+       }
+       
+       private <T extends CdmBase> void testMergeValid(T cdmBase1, T cdmBase2)throws IllegalArgumentException, NullPointerException{
+               if (cdmBase1 == null || cdmBase2 == null){
+                       throw new NullPointerException("Merge arguments must not be (null)");
+               }
+               cdmBase1 = (T)HibernateProxyHelper.deproxy(cdmBase1);
+               cdmBase2 = (T)HibernateProxyHelper.deproxy(cdmBase2);
+               
+               if (cdmBase1.getClass() != cdmBase2.getClass()){
+                       throw new IllegalArgumentException("Merge arguments must be of same type");
+               }
+       }
+       
+       //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();
+               Object roles = factory.getCollectionRolesByEntityParticipant("eu.etaxonomy.cdm.model.name.BotanicalName");
+               CollectionPersister collPersister;
+               try {
+                       collPersister = factory.getCollectionPersister(TaxonNameBase.class.getCanonicalName()+".annotations");
+               } catch (MappingException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+               Statistics statistics = factory.getStatistics();
+               Map allClassMetadata = factory.getAllClassMetadata();
+               logger.debug("");
+               
+       }
+
+       @Override
+       public <T extends CdmBase> T find(Class<T> clazz, int id){
+               Session session;
+               session =  getSession();
+               //session = getSession().getSessionFactory().getCurrentSession();
+               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());
+       }
 
 }