All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nextapp.echo.app.serial.Serializer Maven / Gradle / Ivy

/* 
 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
 * Copyright (C) 2002-2009 NextApp, Inc.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */

package nextapp.echo.app.serial;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Element;

import nextapp.echo.app.MutableStyle;
import nextapp.echo.app.Style;
import nextapp.echo.app.reflect.IntrospectorFactory;
import nextapp.echo.app.reflect.ObjectIntrospector;
import nextapp.echo.app.util.Context;
import nextapp.echo.app.util.DomUtil;

/**
 * Front-end for translating XML component/style to Style instances. 
 */
public class Serializer {
    
    /**
     * Map of ClassLoaders to PropertyLoaders.
     */
    private static final Map classLoaderToPropertyLoaderMap = new HashMap();
    
    /**
     * Creates or retrieves a Serializer.
     * 
     * @param classLoader the ClassLoader to use for 
     *        dynamically loading peer classes
     * @return the Serializer
     */
    public static Serializer forClassLoader(ClassLoader classLoader) {
        synchronized(classLoaderToPropertyLoaderMap) {
            Serializer serializer = (Serializer) classLoaderToPropertyLoaderMap.get(classLoader);
            if (serializer == null) {
                serializer = new Serializer(classLoader);
                classLoaderToPropertyLoaderMap.put(classLoader, serializer);
            }
            return serializer;
        }
    }
    
    /**
     * Mapping for shorthands for java.lang types. 
     */
    private static final Map javaLangTypeMap;
    
    static {
        Map m = new HashMap();
        m.put("b", Boolean.class);
        m.put("i", Integer.class);
        m.put("s", String.class);
        javaLangTypeMap = Collections.unmodifiableMap(m);
    }
    
    private SerialPeerFactory factory;
    private Map typeMap;
    private ClassLoader classLoader;
    
    /**
     * Creates a new Serializer
     * 
     * @param classLoader the ClassLoader to use for instantiation
     */
    private Serializer(final ClassLoader classLoader) {
        super();
        
        this.classLoader = classLoader;
        factory = SerialPeerFactory.forClassLoader(classLoader);
        
        typeMap = new HashMap();
    }

    /**
     * Returns a Class based on an XML type value.
     * The provided type may be a java.lang shorthand, e.g., "s" for string, "b" for boolean.
     * If the provided type is not fully qualified, a standard Echo property type is assumed, e.g.
     * "Extent" will return the nextapp.echo.app.Extent class.
     * If the property type is fully qualified, it will simply be loaded by the classloader. 
     * 
     * @param type the XML type value
     * @return the represented Class
     * @throws ClassNotFoundException in the event that no class exists with the specified type
     */
    public Class getClass(String type) 
    throws ClassNotFoundException {
        // Attempt to retrieve class from core types.
        Class clazz = (Class) javaLangTypeMap.get(type);
        if (clazz != null) {    
            return clazz;
        }
        
        // Attempt to retrieve class from cached types.
        clazz = (Class) typeMap.get(type);
        if (clazz != null) {    
            return clazz;
        }
        
        // If type is shorthand (no package name) prepend "nextapp.echo.app." to type and attempt to load. 
        if (type.indexOf(".") == -1) {
            String echoType = "nextapp.echo.app." + type; 
            try {
                clazz = Class.forName(echoType, true, classLoader);
                typeMap.put(type, clazz);
                return clazz;
            } catch (ClassNotFoundException ex) {
                // Do nothing.
            }
        }

        // Attempt to load specified type.
        clazz = Class.forName(type, true, classLoader);
        typeMap.put(type, clazz);
        return clazz;
    }
    
    /**
     * Retrieves a SerialPropertyPeer specified by a type name in a property DOM element, if possible.
     * Returns null in the event no type name is specified.
     * 
     * @param propertyElement the property element, which may or may not specify a "t" attribute specifying the type name
     * @return the SerialPropertyPeer, if it can be determined
     * @throws SerialException
     */
    public SerialPropertyPeer getSerialPropertyPeer(Element propertyElement) 
    throws SerialException {
        if (!propertyElement.hasAttribute("t")) {
            return null;
        }
        try {
            Class propertyClass = null;
            String type = propertyElement.getAttribute("t");
            propertyClass = getClass(type);
            return (SerialPropertyPeer) factory.getPeerForProperty(propertyClass);
        } catch (ClassNotFoundException ex) {
            throw new SerialException("Error loading class.", ex);
        }    
    }
    
    /**
     * Creates a Style object based on an XML property container.
     * 
     * @param serialContext the SerialContext providing contextual information about the serialization
     * @param componentType the component type for which the Style will be used
     * @param containerElement the DOM element containing the style properties
     * @return the generated Style
     * @throws SerialException
     */
    public Style loadStyle(final SerialContext serialContext, String componentType, Element containerElement) 
    throws SerialException {
        try {
            ObjectIntrospector introspector = IntrospectorFactory.get(componentType, classLoader);
            MutableStyle style = new MutableStyle();

            Context context = new Context() {
                public Object get(Class specificContextClass) {
                    if (specificContextClass == SerialContext.class) {
                        return serialContext;
                    } else if (specificContextClass == PropertyPeerFactory.class) {
                        return factory;
                    }
                    return null;
                }
            };
            
            Element[] pElements = DomUtil.getChildElementsByTagName(containerElement, "p");
            for (int i = 0; i < pElements.length; ++i) {
                // Retrieve property name.
                if (!pElements[i].hasAttribute("n")) {
                    throw new SerialException("Found property without name in component \"" + componentType + "\".", null);
                }
                String name = pElements[i].getAttribute("n");

                SerialPropertyPeer peer = getSerialPropertyPeer(pElements[i]);
                
                int index = -1;
                if (pElements[i].hasAttribute("x")) {
                    index = Integer.parseInt(pElements[i].getAttribute("x"));
                }
                
                if (peer == null) {
                    Class propertyClass = introspector.getPropertyClass(name);
                    if (propertyClass == null) {
                        throw new SerialException("Cannot find class for property: " + componentType + "." + name, null);
                    }
                    peer = (SerialPropertyPeer) factory.getPeerForProperty(propertyClass);
                }
                
                if (peer == null) {
                    // Unsupported property.
                    continue;
                }
                
                Object value = peer.toProperty(context, introspector.getObjectClass(), pElements[i]);

                if (index == -1) {
                    style.set(name, value);
                } else {
                    style.setIndex(name, index, value);
                }
            }
            
            return style;
        } catch (ClassNotFoundException ex) {
            throw new SerialException("Error loading class.", ex);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy