Java tutorial
/* * cBean Copyright 2016, Tom Everett <tom@khubla.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.khubla.cbean.serializer.impl; import java.lang.reflect.Field; import java.util.Hashtable; import org.apache.commons.beanutils.PropertyUtils; import com.khubla.cbean.CBean; import com.khubla.cbean.CBeanException; import com.khubla.cbean.CBeanKey; import com.khubla.cbean.CBeanServer; import com.khubla.cbean.CBeanType; import com.khubla.cbean.annotation.Id; import com.khubla.cbean.annotation.Property; import com.khubla.cbean.cglib.CBeanLazyLoadInterceptor; import com.khubla.cbean.serializer.ClassHasher; import com.khubla.cbean.serializer.FieldSerializer; import com.khubla.cbean.serializer.FieldSerializerFactory; import com.khubla.cbean.serializer.SerializerException; import com.khubla.cbean.serializer.impl.json.JSONFieldSerializerFactory; import net.sf.cglib.proxy.Enhancer; public class DefaultClassHasher implements ClassHasher { /** * get a class instance */ private static Object getInstance(Class<?> clazz) throws CBeanException { try { /* * instance */ return clazz.newInstance(); } catch (final Exception e) { throw new CBeanException(e); } } /** * get a cglib proxy to a new instance */ private static Object getProxiedInstance(Class<?> clazz, String key) throws CBeanException { try { /* * create a proxy */ final Enhancer e = new Enhancer(); e.setSuperclass(clazz); e.setCallback(new CBeanLazyLoadInterceptor(clazz, key)); return e.create(); } catch (final Exception e) { throw new CBeanException(e); } } private final Class<?> clazz; /** * field serializers */ private final FieldSerializerFactory fieldSerializerFactory = new JSONFieldSerializerFactory(); public DefaultClassHasher(Class<?> clazz) { this.clazz = clazz; } /** * get a class as a hash */ @Override public Hashtable<String, String> classToHash(Object o) throws SerializerException { try { /* * update the version */ final Field versionField = CBeanType.getCBeanType(clazz).getVersionField(); if (null != versionField) { Integer version = (Integer) PropertyUtils.getProperty(o, versionField.getName()); if (null == version) { version = new Integer(0); } version = version + 1; PropertyUtils.setProperty(o, versionField.getName(), version); } /* * walk the fields */ final Hashtable<String, String> ret = new Hashtable<String, String>(); final Field[] persistableFields = CBeanType.getCBeanType(clazz).getPersistableFields(); for (int i = 0; i < persistableFields.length; i++) { final Field field = persistableFields[i]; final Class<?> fieldClazz = field.getType(); final FieldSerializer serializer = fieldSerializerFactory.getFieldSerializer(field); final Property property = field.getAnnotation(Property.class); if (null != serializer) { final String value = serializer.serialize(o, field); if (null != value) { /* * only save the value if it's not null */ ret.put(field.getName(), value); } else { if (property.nullable() == false) { throw new CBeanException("Field '" + field.getName() + "' is not nullable"); } if (null != field.getAnnotation(Id.class)) { throw new CBeanException("Field '" + field.getName() + "' is an Id and cannot be null"); } } } else if (CBean.isEntity(fieldClazz)) { /* * recurse */ if (property.cascadeSave()) { final CBean<Object> cBean = CBeanServer.getInstance().getCBean(fieldClazz); final Object oo = PropertyUtils.getProperty(o, field.getName()); if ((null == oo) && (property.nullable() == false)) { throw new CBeanException("Field '" + field.getName() + "' is not nullable"); } cBean.save(oo); final String value = cBean.getId(oo); ret.put(field.getName(), value); } } else { throw new SerializerException("Unsupported serialization type '" + fieldClazz.getName() + "'"); } } return ret; } catch (final Exception e) { throw new SerializerException(e); } } @Override public void delete(Object o) throws SerializerException { try { final Field[] persistableFields = CBeanType.getCBeanType(clazz).getPersistableFields(); for (int i = 0; i < persistableFields.length; i++) { final Field field = persistableFields[i]; final Class<?> fieldClazz = field.getType(); final Property property = field.getAnnotation(Property.class); final FieldSerializer serializer = fieldSerializerFactory.getFieldSerializer(field); if (null != serializer) { serializer.delete(o, field); } else if (CBean.isEntity(fieldClazz)) { /* * delete fields that are classes */ if (property.cascadeDelete()) { final CBean<Object> cBean = CBeanServer.getInstance().getCBean(fieldClazz); final Object oo = PropertyUtils.getProperty(o, field.getName()); /* * only recurse if oo is not null */ if (null != oo) { final String key = cBean.getId(oo); cBean.delete(new CBeanKey(key)); } } } } } catch (final Exception e) { throw new SerializerException(e); } } /** * populate class from hash */ @Override public Object hashToClass(Hashtable<String, String> hashtable) throws SerializerException { try { /* * get a proxied class to use */ // final Object ret = getProxiedInstance(clazz); final Object ret = getInstance(clazz); /* * get the fields */ final Field[] persistableFields = CBeanType.getCBeanType(clazz).getPersistableFields(); for (int i = 0; i < persistableFields.length; i++) { final Field field = persistableFields[i]; final Class<?> fieldClazz = field.getType(); final FieldSerializer serializer = fieldSerializerFactory.getFieldSerializer(field); final String fieldValue = hashtable.get(field.getName()); if (null != serializer) { serializer.deserialize(ret, field, fieldValue); } else if (CBean.isEntity(fieldClazz)) { /* * recurse */ final Property property = field.getAnnotation(Property.class); if (property.cascadeLoad()) { final CBean<Object> cBean = CBeanServer.getInstance().getCBean(fieldClazz); /* * this load may come back as a null. If it does, the contained object referred to has been deleted. This is ok, just set the value to null. */ final Object o = cBean.load(new CBeanKey(fieldValue)); PropertyUtils.setProperty(ret, field.getName(), o); } else { final Object o = getProxiedInstance(fieldClazz, fieldValue); PropertyUtils.setProperty(ret, field.getName(), o); } } else { throw new SerializerException("Unsupported serialization type '" + fieldClazz.getName() + "'"); } } return ret; } catch (final Exception e) { throw new SerializerException(e); } } }