org.openmobster.core.mobileObject.xml.MobileObjectSerializer.java Source code

Java tutorial

Introduction

Here is the source code for org.openmobster.core.mobileObject.xml.MobileObjectSerializer.java

Source

/**
 * Copyright (c) {2003,2011} {openmobster@gmail.com} {individual contributors as indicated by the @authors tag}.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package org.openmobster.core.mobileObject.xml;

import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.beans.PropertyDescriptor;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringTokenizer;
import java.util.Map;
import java.util.HashMap;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import org.openmobster.core.common.XMLUtilities;
import org.openmobster.core.common.Utilities;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.mapper.MapperWrapper;

/**
 * @author openmobster@gmail.com
 */
public class MobileObjectSerializer {
    private static Logger log = Logger.getLogger(MobileObjectSerializer.class);

    private XStream streamer;
    private XStream stateChecker;

    public MobileObjectSerializer() {

    }

    public void start() {
        this.streamer = new XStream(new MobileObjectDriver()) {
            protected MapperWrapper wrapMapper(MapperWrapper next) {
                return new MobileObjectMapperWrapper(next);
            }
        };

        this.stateChecker = new XStream();
    }

    public void stop() {
        this.streamer = null;
    }

    public String serialize(Object object) {
        try {
            String coreXml = this.stateChecker.toXML(object);
            if (coreXml.contains("<null/>")) {
                throw new IllegalStateException("The Object being mobilized has Illegal Null Array Elements!!");
            }

            return this.streamer.toXML(object);
        } catch (Exception e) {
            log.error(this, e);
            throw new RuntimeException(e);
        }
    }

    public Object deserialize(Class pojoClazz, String deviceXml) {
        try {
            Object pojo = null;

            pojo = pojoClazz.newInstance();

            Document root = XMLUtilities.parse(deviceXml);

            //Parse the Object Meta Data
            List<ArrayMetaData> objectMetaData = new ArrayList<ArrayMetaData>();
            NodeList metaDataNodes = root.getElementsByTagName("array-metadata");
            if (metaDataNodes != null) {
                for (int i = 0; i < metaDataNodes.getLength(); i++) {
                    Element metaDataElement = (Element) metaDataNodes.item(i);
                    Element arrayUriElement = (Element) metaDataElement.getElementsByTagName("uri").item(0);
                    Element arrayLengthElement = (Element) metaDataElement.getElementsByTagName("array-length")
                            .item(0);
                    Element arrayClassElement = (Element) metaDataElement.getElementsByTagName("array-class")
                            .item(0);

                    ArrayMetaData arrayMetaData = new ArrayMetaData();
                    arrayMetaData.arrayUri = arrayUriElement.getTextContent().trim();
                    arrayMetaData.arrayLength = Integer.parseInt(arrayLengthElement.getTextContent().trim());
                    arrayMetaData.arrayClass = arrayClassElement.getTextContent().trim();

                    objectMetaData.add(arrayMetaData);
                }
            }

            //Set the fields
            Element fieldsElement = (Element) root.getElementsByTagName("fields").item(0);
            if (fieldsElement != null) {
                NodeList fieldNodes = fieldsElement.getElementsByTagName("field");
                if (fieldNodes != null) {
                    for (int i = 0; i < fieldNodes.getLength(); i++) {
                        Element fieldElement = (Element) fieldNodes.item(i);

                        String name = ((Element) fieldElement.getElementsByTagName("name").item(0))
                                .getTextContent();

                        String value = ((Element) fieldElement.getElementsByTagName("value").item(0))
                                .getTextContent();

                        String uri = ((Element) fieldElement.getElementsByTagName("uri").item(0)).getTextContent();

                        String expression = this.parseExpression(uri);

                        if (expression.indexOf('.') == -1 && expression.indexOf('[') == -1) {
                            //Simple Property
                            PropertyDescriptor metaData = PropertyUtils.getPropertyDescriptor(pojo, expression);

                            if (metaData == null || metaData.getPropertyType() == null) {
                                log.error("******************************");
                                log.error("MetaData Null For: " + expression);
                                log.error("Field Not Found on the MobileBean");
                                log.error("******************************");
                                continue;
                            }

                            if (metaData.getPropertyType().isArray()
                                    && metaData.getPropertyType().getComponentType().isAssignableFrom(byte.class)) {
                                BeanUtils.setProperty(pojo, expression, Utilities.decodeBinaryData(value));
                            } else {
                                BeanUtils.setProperty(pojo, expression, value);
                            }
                        } else {
                            //Nested Property
                            this.setNestedProperty(pojo, expression, value, objectMetaData);
                        }
                    }
                }
            }

            return pojo;
        } catch (Exception e) {
            log.error(this, e);
            throw new RuntimeException(e);
        }
    }

    //---------------------------------------------------------------------------------------------------------------------------
    private String parseExpression(String fieldUri) {
        String expression = "";
        StringBuilder buffer = new StringBuilder();

        StringTokenizer st = new StringTokenizer(fieldUri, "/");
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (!token.contains(".")) {
                buffer.append(token + "/");
            }
        }

        String local = buffer.toString();
        if (local.endsWith("/")) {
            local = local.substring(0, local.length() - 1);
        }

        expression = local.replace("/", ".");

        return expression;
    }

    private void setNestedProperty(Object mobileBean, String nestedProperty, String value,
            List<ArrayMetaData> objectMetaData) {
        try {
            StringTokenizer st = new StringTokenizer(nestedProperty, ".");
            Object courObj = mobileBean;
            StringBuilder propertyPath = new StringBuilder();

            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                propertyPath.append("/" + token);

                PropertyDescriptor metaData = PropertyUtils.getPropertyDescriptor(courObj, token);
                if (token.indexOf('[') != -1 && token.indexOf(']') != -1) {
                    String indexedPropertyName = token.substring(0, token.indexOf('['));
                    metaData = PropertyUtils.getPropertyDescriptor(courObj, indexedPropertyName);
                }

                if (metaData == null) {
                    log.error("******************************");
                    log.error("MetaData Null For: " + token);
                    log.error("Field Not Found on the MobileBean");
                    log.error("******************************");
                    continue;
                }

                if (!st.hasMoreTokens()) {
                    if (Collection.class.isAssignableFrom(metaData.getPropertyType()) || (metaData.getPropertyType()
                            .isArray()
                            && !metaData.getPropertyType().getComponentType().isAssignableFrom(byte.class))) {
                        //An IndexedProperty
                        this.initializeIndexedProperty(courObj, token, metaData, objectMetaData,
                                propertyPath.toString());

                        if (metaData.getPropertyType().isArray()) {
                            PropertyUtils.setNestedProperty(mobileBean, nestedProperty,
                                    ConvertUtils.convert(value, metaData.getPropertyType().getComponentType()));
                        } else {
                            PropertyUtils.setNestedProperty(mobileBean, nestedProperty,
                                    ConvertUtils.convert(value, metaData.getPropertyType()));
                        }
                    } else {
                        //A Simple Property                                          
                        if (metaData.getPropertyType().isArray()
                                && metaData.getPropertyType().getComponentType().isAssignableFrom(byte.class)) {
                            BeanUtils.setProperty(mobileBean, nestedProperty, Utilities.decodeBinaryData(value));
                        } else {
                            PropertyUtils.setNestedProperty(mobileBean, nestedProperty,
                                    ConvertUtils.convert(value, metaData.getPropertyType()));
                        }
                    }
                } else {
                    if (Collection.class.isAssignableFrom(metaData.getPropertyType())
                            || metaData.getPropertyType().isArray()) {
                        //An IndexedProperty   
                        courObj = this.initializeIndexedProperty(courObj, token, metaData, objectMetaData,
                                propertyPath.toString());
                    } else {
                        //A Simple Property
                        courObj = this.initializeSimpleProperty(courObj, token, metaData);
                    }
                }
            }
        } catch (Exception e) {
            log.info("---------------------------------------------------");
            log.info("Blowing Up on---------" + nestedProperty);
            log.info("---------------------------------------------------");
            log.error(this, e);
            throw new RuntimeException(e);
        }
    }

    private Object initializeSimpleProperty(Object parentObject, String property,
            PropertyDescriptor propertyMetaData) throws Exception {
        Object propertyValue = null;

        //A Regular Property
        propertyValue = PropertyUtils.getProperty(parentObject, property);
        if (propertyValue == null) {
            Object newlyInitialized = propertyMetaData.getPropertyType().newInstance();
            PropertyUtils.setProperty(parentObject, property, newlyInitialized);
            propertyValue = newlyInitialized;
        }

        return propertyValue;
    }

    private Object initializeIndexedProperty(Object parentObject, String property,
            PropertyDescriptor propertyMetaData, List<ArrayMetaData> objectMetaData, String propertyPath)
            throws Exception {
        Object element = null;

        //ArrayUri
        String arrayUri = null;
        Integer arrayIndex = 0;
        if (propertyPath.endsWith("]")) {
            int lastIndex = propertyPath.lastIndexOf('[');
            arrayUri = propertyPath.substring(0, lastIndex);
            arrayIndex = Integer.parseInt(propertyPath.substring(lastIndex + 1, propertyPath.length() - 1).trim());
        }
        ArrayMetaData arrayMetaData = null;
        for (ArrayMetaData local : objectMetaData) {
            if (local.arrayUri.equals(arrayUri)) {
                arrayMetaData = local;
                break;
            }
        }

        //Find the Class of the elementType
        String elementTypeName = arrayMetaData.arrayClass;
        Class elementType = null;
        if (elementTypeName != null && elementTypeName.trim().length() > 0 && !elementTypeName.equals("null")) {
            elementType = Thread.currentThread().getContextClassLoader().loadClass(arrayMetaData.arrayClass);
        } else {
            //Figure out the element type from the Property Information
            //This happens when a brand new object is created on the device and is being synced
            //with the backend
            //The MobileObject Framework on the device does not know about any Class level information
            //of the remote bean
            //The Limitation of this is that:
            //
            //* Indexed Properties if Collections must be Parameterized with Concrete Types
            //* Indexed Properties if Arrays must be Arrays of Concrete Types
            if (!propertyMetaData.getPropertyType().isArray()) {
                ParameterizedType returnType = (ParameterizedType) propertyMetaData.getReadMethod()
                        .getGenericReturnType();
                Type[] actualTypes = returnType.getActualTypeArguments();
                for (Type actualType : actualTypes) {
                    elementType = (Class) actualType;
                }
            } else {
                elementType = propertyMetaData.getPropertyType().getComponentType();
            }
        }

        //An IndexedProperty
        Object indexedProperty = PropertyUtils.getProperty(parentObject, propertyMetaData.getName());

        //Initialize the IndexedProperty (An Array or Collection)
        if (propertyMetaData.getPropertyType().isArray()) {
            int arraySize = arrayMetaData.arrayLength;
            if (indexedProperty == null) {
                //Initialize the Array with Size from Object Meta Data               
                PropertyUtils.setProperty(parentObject, propertyMetaData.getName(),
                        Array.newInstance(elementType, arraySize));
            } else {
                //Make sure the Array Size matches
                int actualSize = Array.getLength(indexedProperty);
                if (actualSize != arraySize) {
                    //Re-set the existing Array
                    PropertyUtils.setProperty(parentObject, propertyMetaData.getName(),
                            Array.newInstance(elementType, arraySize));
                }
            }
        } else {
            if (indexedProperty == null) {
                //Handle Collection Construction
                PropertyUtils.setProperty(parentObject, propertyMetaData.getName(), new ArrayList());
            }
        }

        //Check to see if the index specified by the field requires creation of new
        //element
        indexedProperty = PropertyUtils.getProperty(parentObject, propertyMetaData.getName());

        if (!propertyMetaData.getPropertyType().isArray()) {
            try {
                element = PropertyUtils.getIndexedProperty(parentObject, property);
            } catch (IndexOutOfBoundsException iae) {
                Object newlyInitialized = elementType.newInstance();
                ((Collection) indexedProperty).add(newlyInitialized);
                element = newlyInitialized;
            }
        } else {
            element = PropertyUtils.getIndexedProperty(parentObject, property);
            if (element == null) {
                Object newlyInitialized = elementType.newInstance();
                Array.set(indexedProperty, arrayIndex, newlyInitialized);
                element = newlyInitialized;
            }
        }

        return element;
    }
}