name.martingeisse.common.javascript.serialize.BeanToJavascriptObjectSerializer.java Source code

Java tutorial

Introduction

Here is the source code for name.martingeisse.common.javascript.serialize.BeanToJavascriptObjectSerializer.java

Source

/**
 * Copyright (c) 2010 Martin Geisse
 *
 * This file is distributed under the terms of the MIT license.
 */

package name.martingeisse.common.javascript.serialize;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import name.martingeisse.common.javascript.JavascriptAssembler;
import org.apache.commons.beanutils.PropertyUtils;

/**
 * This class turns Java beans into Javascript objects. It optionally only
 * serializes specific fields, and optionally allows subclasses to add
 * custom generated fields that are not actually present in the object being
 * serialized. Calling code may also specify a list of requested generated
 * fields which subclasses should honor.
 * 
 * Subclasses may distinguish bean property names (used to read properties)
 * and serialized property names (used in the JSON output). Whenever property
 * names are explicitly specified, the serialized names are expected.
 * 
 * @param <T> the type of values this serializer can handle
 */
public class BeanToJavascriptObjectSerializer<T> extends AbstractJavascriptSerializer<T> {

    /**
     * the fieldNames
     */
    private String[] fieldNames;

    /**
     * the requestedGeneratedFieldNames
     */
    private String[] requestedGeneratedFieldNames;

    /**
     * Constructor.
     */
    public BeanToJavascriptObjectSerializer() {
    }

    /**
     * Constructor.
     * @param fieldNames the fields to serialize
     */
    public BeanToJavascriptObjectSerializer(final String... fieldNames) {
        this.fieldNames = fieldNames;
    }

    /**
     * Constructor.
     * @param fieldNames the fields to serialize
     * @param requestedGeneratedFieldNames the fields to generate
     */
    public BeanToJavascriptObjectSerializer(String[] fieldNames, String[] requestedGeneratedFieldNames) {
        this.fieldNames = fieldNames;
        this.requestedGeneratedFieldNames = requestedGeneratedFieldNames;
    }

    /**
     * Getter method for the fieldNames.
     * @return the fieldNames
     */
    public final String[] getFieldNames() {
        return fieldNames;
    }

    /**
     * Setter method for the fieldNames.
     * @param fieldNames the fieldNames to set
     */
    public final void setFieldNames(final String... fieldNames) {
        this.fieldNames = fieldNames;
    }

    /**
     * Getter method for the requestedGeneratedFieldNames.
     * @return the requestedGeneratedFieldNames
     */
    public String[] getRequestedGeneratedFieldNames() {
        return requestedGeneratedFieldNames;
    }

    /**
     * Setter method for the requestedGeneratedFieldNames.
     * @param requestedGeneratedFieldNames the requestedGeneratedFieldNames to set
     */
    public void setRequestedGeneratedFieldNames(String[] requestedGeneratedFieldNames) {
        this.requestedGeneratedFieldNames = requestedGeneratedFieldNames;
    }

    /* (non-Javadoc)
     * @see name.martingeisse.common.javascript.serialize.IJavascriptSerializer#serialize(java.lang.Object, name.martingeisse.common.javascript.JavascriptAssembler)
     */
    @Override
    public void serialize(final T bean, final JavascriptAssembler assembler) {
        try {
            assembler.beginObject();
            if (fieldNames == null) {
                serializeAllFields(bean, assembler);
            } else {
                serializeSpecificFields(bean, assembler);
            }
            serializeGeneratedFields(bean, assembler);
            assembler.endObject();
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 
     */
    private void serializeAllFields(final T bean, final JavascriptAssembler assembler) throws Exception {
        for (final PropertyDescriptor property : PropertyUtils.getPropertyDescriptors(bean)) {
            final String beanPropertyName = property.getName();
            if (beanPropertyName.equals("class")) {
                continue;
            }
            final String serializedFieldName = mapPropertyNameToSerializedName(beanPropertyName);
            final Object value = property.getReadMethod().invoke(bean);
            assembler.prepareObjectProperty(serializedFieldName);
            serializeFieldValue(bean, assembler, beanPropertyName, serializedFieldName, value);
        }
    }

    /**
     * 
     */
    private void serializeSpecificFields(final T bean, final JavascriptAssembler assembler) throws Exception {
        serializeFields(bean, assembler, fieldNames);
    }

    /**
     * This method is a stub that allows subclasses to add custom generated fields not
     * actually present in the object being serialized. The default implementation does nothing.
     * 
     * @param bean the bean being serialized
     * @param assembler the assembler
     */
    protected void serializeGeneratedFields(final T bean, final JavascriptAssembler assembler) {
    }

    /**
     * Serializes the specified bean fields.
     * @param bean the bean to get values from
     * @param assembler the Javascript assembler to use
     * @param fieldNames the names of the fields to serialize
     */
    public void serializeFields(final Object bean, final JavascriptAssembler assembler,
            final String... fieldNames) {
        try {
            for (final String serializedName : fieldNames) {
                String beanPropertyName = mapSerializedNameToPropertyName(serializedName);
                final PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean,
                        beanPropertyName);
                if (propertyDescriptor == null) {
                    throw new RuntimeException("no such property: " + serializedName + " (bean property name: "
                            + beanPropertyName + ")");
                }
                final Method readMethod = propertyDescriptor.getReadMethod();
                final Object value = readMethod.invoke(bean);
                assembler.prepareObjectProperty(serializedName);
                serializeFieldValue(bean, assembler, beanPropertyName, serializedName, value);
            }
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * This method is used to serialize a single field value. The default implementation
     * uses {@link JavascriptAssembler#appendPrimitive(Object)}.
     * 
     * @param bean the bean
     * @param assembler the Javascript assembler
     * @param beanPropertyName the bean property name of the field
     * @param serializedFieldName the serialized name of the field
     * @param value the field value
     */
    protected void serializeFieldValue(final Object bean, final JavascriptAssembler assembler,
            final String beanPropertyName, final String serializedFieldName, final Object value) {
        assembler.appendPrimitive(value);
    }

    /**
     * Maps the serialized name of a property to its bean property name.
     * The default implementation just returns the serialized name.
     * 
     * @param serializedName the serialized name
     * @return the bean property name
     */
    protected String mapSerializedNameToPropertyName(String serializedName) {
        return serializedName;
    }

    /**
     * Maps the bean property name of a property to its serialized
     * property name. The default implementation just returns the bean property name.
     * 
     * @param propertyName the bean property name
     * @return the serialized name
     */
    protected String mapPropertyNameToSerializedName(String propertyName) {
        return propertyName;
    }

}