com.khubla.cbean.serializer.impl.DefaultClassHasher.java Source code

Java tutorial

Introduction

Here is the source code for com.khubla.cbean.serializer.impl.DefaultClassHasher.java

Source

/*
 * 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);
        }
    }
}