Package javolution.xml

Source Code of javolution.xml.XMLBinding

/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2006 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.xml;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javolution.lang.Configurable;
import javolution.lang.Reflection;
import javolution.lang.Reusable;
import javolution.text.CharArray;
import javolution.text.TextBuilder;
import javolution.text.TextFormat;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
import javolution.util.FastTable;
import javolution.xml.XMLFormat.InputElement;
import javolution.xml.XMLFormat.OutputElement;
import javolution.xml.stream.XMLStreamException;
import javolution.xml.stream.XMLStreamReader;
import javolution.xml.stream.XMLStreamWriter;

/**
* <p> This class represents the binding between Java classes and
*     their XML representation ({@link XMLFormat}).</p>
*    
* <p> Custom XML bindings can also be used to alias class names and
*     ensure that the XML representation is:<ul>
*     <li> Impervious to obfuscation.</li>
*     <li> Unaffected by any class refactoring.</li>
*     <li> Can be mapped to multiple implementations. For example:[code]
*    
*     // Creates a binding to serialize Swing components into high-level XML
*     // and deserialize the same XML into SWT components.
*     XMLBinding swingBinding = new XMLBinding();
*     swingBinding.setAlias(javax.swing.JButton.class, "Button");
*     swingBinding.setAlias(javax.swing.JTable.class, "Table");
*     ...
*     XMLBinding swtBinding = new XMLBinding();
*     swtBinding.setAlias(org.eclipse.swt.widgets.Button.class, "Button");
*     swtBinding.setAlias(org.eclipse.swt.widgets.Table.class, "Table");
*     ...
*    
*     // Writes Swing Desktop to XML.
*     XMLObjectWriter writer = new XMLObjectWriter().setBinding(swingBinding);
*     writer.setOutput(new FileOutputStream("C:/desktop.xml"));
*     writer.write(swingDesktop, "Desktop", SwingDesktop.class);
*     writer.close();
*
*     // Reads back high-level XML to a SWT implementation!   
*     XMLObjectReader reader = new XMLObjectReader().setXMLBinding(swtBinding);
*     reader.setInput(new FileInputStream("C:/desktop.xml"));
*     SWTDesktop swtDesktop = reader.read("Desktop", SWTDesktop.class);
*     reader.close();
*     [/code]</li>
*     </ul></p>       
*    
* <p> More advanced bindings can also be created through sub-classing.[code]
*
*     // XML binding using reflection.
*     public ReflectionBinding extends XMLBinding {
*         protected XMLFormat getFormat(Class forClass) {
*             Field[] fields = forClass.getDeclaredFields();
*             return new XMLReflectionFormat(fields);
*         }
*     }
*    
*     // XML binding read from DTD input source.
*     public DTDBinding extends XMLBinding {
*         public DTDBinding(InputStream dtd) {
*             ...
*         }
*     }
*    
*     // XML binding overriding default formats.
*     public MyBinding extends XMLBinding {
*         // Non-static formats use unmapped XMLFormat instances.
*         XMLFormat<String> myStringFormat = new XMLFormat<String>(null) {...}
*         XMLFormat<Collection> myCollectionFormat = new XMLFormat<Collection>(null) {...}
*         protected XMLFormat getFormat(Class forClass) throws XMLStreamException {
*             if (String.class.equals(forClass))
*                  return myStringFormat;
*             if (Collection.class.isAssignableFrom(forClass))
*                  return myCollectionFormat;
*             return super.getFormat(cls);
*         }
*     }
*     [/code]
*     
* <p> The default XML binding implementation supports all static XML formats
*     (static members of the classes being mapped) as well as the
*     following types:<ul>
*        <li><code>java.lang.Object</code> (empty element)</li>
*        <li><code>java.lang.Class</code></li>
*        <li><code>java.lang.String</code></li>
*        <li><code>java.lang.Appendable</code></li>
*        <li><code>java.util.Collection</code></li>
*        <li><code>java.util.Map</code></li>
*        <li><code>java.lang.Object[]</code></li>
*        <li> all primitive types wrappers (e.g.
*            <code>Boolean, Integer ...</code>)</li>
*        </ul></p>
*         
* @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 5.4, December 1, 2009
*/
public class XMLBinding implements Reusable, XMLSerializable {

    /**
     * Holds the default instance used by readers/writers (thread-safe).
     */
    static final XMLBinding DEFAULT = new XMLBinding();

    /**
     * Holds the class attribute.
     */
    private QName _classAttribute = QName.valueOf("class");

    /**
     * Holds the class to alias (QName) mapping.
     */
    private final FastMap _classToAlias = new FastMap();

    /**
     * Holds the alias (QName) to class mapping.
     */
    private final FastMap _aliasToClass = new FastMap();

    /**
     * Default constructor.
     */
    public XMLBinding() {
    }

    /**
     * Sets the qualified alias for the specified class.
     *
     * @param cls the class being aliased.
     * @param qName the qualified name.
     */
    public void setAlias(Class cls, QName qName) {
        _classToAlias.put(cls, qName);
        _aliasToClass.put(qName, cls);
    }

    /**
     * Convenient method equivalent to {@link #setAlias(Class, QName)
     * setAlias(cls, QName.valueOf(alias))}.
     *
     * @param cls the class being aliased.
     * @param alias the alias for the specified class.
     */
    public final void setAlias(Class cls, String alias) {
        setAlias(cls, QName.valueOf(alias));
    }

    /**
     * Sets the qualified name of the attribute holding the
     * class identifier. If the local name is <code>null</code> the class
     * attribute is never read/written (which may prevent unmarshalling).
     *
     * @param classAttribute the qualified name of the class attribute or
     *        <code>null<code>.
     */
    public void setClassAttribute(QName classAttribute) {
        _classAttribute = classAttribute;
    }

    /**
     * Convenience method equivalent to {@link #setClassAttribute(QName)
     * setClassAttribute(QName.valueOf(name))}.
     *
     * @param name the name of the class attribute or <code>null<code>.
     */
    public final void setClassAttribute(String name) {
        setClassAttribute(name == null ? null : QName.valueOf(name));
    }

    /**
     * Returns the XML format for the specified class/interface.
     * The default implementation returns the {@link XMLFormat#getInstance}
     * for the specified class.
     *
     * @param forClass the class for which the XML format is returned.
     * @return the XML format for the specified class (never <code>null</code>).
     * @throws XMLStreamException if there is no format for the
     *         specified class.
     */
    protected XMLFormat getFormat(Class forClass) throws XMLStreamException  {
         return XMLFormat.getInstance(forClass);
    }

    /**
     * Reads the class corresponding to the current XML element.
     *
     * This method is called by {@link XMLFormat.InputElement#getNext()}
     * {@link XMLFormat.InputElement#get(String)} and
     * {@link XMLFormat.InputElement#get(String, String)} to retrieve the
     * Java class corresponding to the current XML element.
     *
     * If <code>useAttributes</code> is set, the default implementation
     * reads the class name from the class attribute; otherwise the class
     * name (or alias) is read from the current element qualified name.
     *
     * @param reader the XML stream reader.
     * @param useAttributes indicates if the element's attributes should be
     *        used to identify the class (e.g. when the element name is
     *        specified by the user then attributes have to be used).
     * @return the corresponding class.
     * @throws XMLStreamException
     */
    protected Class readClass(XMLStreamReader reader, boolean useAttributes)
            throws XMLStreamException {
        QName classQName;
        if (useAttributes) {
            if (_classAttribute == null)
                throw new XMLStreamException(
                        "Binding has no class attribute defined, cannot retrieve class");
            classQName = QName.valueOf(reader.getAttributeValue(_classAttribute
                    .getNamespaceURI(), _classAttribute.getLocalName()));
            if (classQName == null)
                throw new XMLStreamException(
                        "Cannot retrieve class (class attribute not found)");
        } else {
            classQName = QName.valueOf(reader.getNamespaceURI(), reader
                    .getLocalName());
        }

        // Searches aliases with namespace URI.
        Class cls = (Class) _aliasToClass.get(classQName);
        if (cls != null)
            return cls;

        // Searches aliases without namespace URI.
        cls = (Class) _aliasToClass.get(QName.valueOf(classQName.getLocalName()));
        if (cls != null)
            return cls;

        // Finally convert the qualified name to a class (ignoring namespace URI).
        cls = Reflection.getInstance().getClass(classQName.getLocalName());
        if (cls == null)
            throw new XMLStreamException("Class " + classQName.getLocalName() +
                    " not found (see javolution.lang.Reflection to support additional class loader)");
        _aliasToClass.put(classQName, cls);
        return cls;
    }

    /**
     * Writes the specified class to the current XML element attributes or to
     * a new element if the element attributes cannot be used.
     *
     * This method is called by
     * {@link XMLFormat.OutputElement#add(Object)} and
     * {@link XMLFormat.OutputElement#add(Object, String)} and
     * {@link XMLFormat.OutputElement#add(Object, String, String)} to
     * identify the Java class corresponding to the XML element.
     *
     *
     * @param cls the class to be written.
     * @param writer the XML stream writer.
     * @param useAttributes indicates if the element's attributes should be
     *        used to identify the class (e.g. when the element name is
     *        specified by the user then attributes have to be used).
     * @throws XMLStreamException
     */
    protected void writeClass(Class cls, XMLStreamWriter writer,
            boolean useAttributes) throws XMLStreamException {
        QName qName = (QName) _classToAlias.get(cls);
        String name = qName != null ? qName.toString() : cls.getName();
        if (useAttributes) {
            if (_classAttribute == null)
                return;
            if (_classAttribute.getNamespaceURI() == null) {
                writer.writeAttribute(_classAttribute.getLocalName(),
                        QName.j2meToCharSeq(name));
            } else {
                writer.writeAttribute(_classAttribute.getNamespaceURI(),
                        _classAttribute.getLocalName(), QName.j2meToCharSeq(name));
            }
        } else {
            if (qName != null) {
              if(qName.getNamespaceURI() == null) {
                writer.writeStartElement(qName.getLocalName());
              } else {
                writer.writeStartElement(qName.getNamespaceURI(), qName.getLocalName());
              }
            } else {
                writer.writeStartElement(QName.j2meToCharSeq(name));
            }
        }
    }

    // Implements Reusable.
    public void reset() {
        _classAttribute = QName.valueOf("class");
        _aliasToClass.reset();
        _classToAlias.reset();
    }

    //////////////////////////////////////////////////
    // PREDEFINED FORMATS (LOADED ONLY IF REQUIRED) //
    //////////////////////////////////////////////////

    /**
     * Holds the static XML format for <code>java.lang.Object.class</code> instances.
     * The XML representation consists of the text representation of the object
     * as a "value" attribute.
     */
    static final XMLFormat OBJECT_XML = new XMLFormat(Object.class) {
      public boolean isReferenceable() {
            return false; // Always by value (immutable).
        }

        public Object newInstance(Class cls,
                javolution.xml.XMLFormat.InputElement xml)
                throws XMLStreamException {
            TextFormat format = TextFormat.getInstance(cls);
            if (!format.isParsingSupported())
                throw new XMLStreamException("No XMLFormat or TextFormat (with parsing supported) for instances of " + cls);
            CharArray value = xml.getAttribute("value");
            if (value == null) throw new XMLStreamException("Missing value attribute (to be able to parse the instance of " + cls + ")");
            return format.parse(value);
        }

        public void read(InputElement xml, Object obj)
                throws XMLStreamException {
            // Do nothing.
        }

        public void write(Object obj, OutputElement xml)
                throws XMLStreamException {
            TextBuilder tmp = TextBuilder.newInstance();
            try {
                TextFormat.getInstance(obj.getClass()).format(obj, tmp);
                xml.setAttribute("value", tmp);
            } finally {
                TextBuilder.recycle(tmp);
            }
        }
    };


    /**
     * Holds the default XML representation for <code>java.util.Collection</code>
     * instances. This representation consists of nested XML elements one for
     * each element of the collection. The elements' order is defined by
     * the collection iterator order. Collections are deserialized using their
     * default constructor.
     */
    static final XMLFormat COLLECTION_XML = new XMLFormat(
            java.util.Collection.class) {

        public void read(InputElement xml, Object obj)
                throws XMLStreamException {
            Collection collection = (Collection) obj;
            while (xml.hasNext()) {
                collection.add(xml.getNext());
            }
        }

        public void write(Object obj, OutputElement xml)
                throws XMLStreamException {
            Collection collection = (Collection) obj;
            for (Iterator i = collection.iterator(); i.hasNext();) {
                xml.add(i.next());
            }
        }
    };

    /**
     * Holds the default XML representation for <code>java.util.Map</code>
     * instances. This representation consists of key/value pair as nested
     * XML elements. For example:[code]
     * <javolution.util.FastMap>
     *     <Key class="java.lang.String" value="ONE"/>
     *     <Value class="java.lang.Integer" value="1"/>
     *     <Key class="java.lang.String" value="TWO"/>
     *     <Value class="java.lang.Integer" value="2"/>
     *     <Key class="java.lang.String" value="THREE"/>
     *     <Value class="java.lang.Integer" value="3"/>
     * </javolution.util.FastMap>[/code]
     *
     * The elements' order is defined by the map's entries iterator order.
     * Maps are deserialized using their default constructor.
     */
    static final XMLFormat MAP_XML = new XMLFormat(java.util.Map.class) {

        public void read(InputElement xml, Object obj)
                throws XMLStreamException {
            final Map map = (Map) obj;
            while (xml.hasNext()) {
                Object key = xml.get("Key");
                Object value = xml.get("Value");
                map.put(key, value);
            }
        }

        public void write(Object obj, OutputElement xml)
                throws XMLStreamException {
            final Map map = (Map) obj;
            for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
                Map.Entry entry = (Map.Entry) it.next();
                xml.add(entry.getKey(), "Key");
                xml.add(entry.getValue(), "Value");
            }
        }
    };

}
TOP

Related Classes of javolution.xml.XMLBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.