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

javolution.xml.XMLBinding Maven / Gradle / Ivy

The newest version!
/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2012 - 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 javolution.util.FastMap;
import javolution.xml.stream.XMLStreamException;
import javolution.xml.stream.XMLStreamReader;
import javolution.xml.stream.XMLStreamWriter;

/**
 * 

This class represents the binding between Java classes and * their XML representation ({@link XMLFormat}).

* *

Custom XML bindings can also be used to alias class names and * ensure that the XML representation is:

    *
  • Impervious to obfuscation.
  • *
  • Unaffected by any class refactoring.
  • *
  • 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]
  • *

* *

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 myStringFormat = new XMLFormat(null) {...} * XMLFormat myCollectionFormat = new XMLFormat(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] * *

The default XML binding implementation supports all static XML formats * (static members of the classes being mapped) as well as the * following types:

    *
  • java.lang.Object (empty element)
  • *
  • java.lang.Class
  • *
  • java.lang.String
  • *
  • java.lang.Appendable
  • *
  • java.util.Collection
  • *
  • java.util.Map
  • *
  • java.lang.Object[]
  • *
  • all primitive types wrappers (e.g. * Boolean, Integer ...)
  • *

* * @author Jean-Marie Dautelle * @version 5.4, December 1, 2009 */ public class XMLBinding implements 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, QName> _classToAlias = new FastMap, QName>(); /** * 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 null the class * attribute is never read/written (which may prevent unmarshalling). * * @param classAttribute the qualified name of the class attribute or * null. */ 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 null. */ 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 XMLContext#getFormat} * for the specified class. * * @param forClass the class for which the XML format is returned. * @return the XML format for the specified class (never null). */ protected XMLFormat getFormat(Class forClass) throws XMLStreamException { return XMLContext.getFormat(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 useAttributes 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 { try { 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 = _aliasToClass.get(classQName); if (cls != null) return cls; // Searches aliases without namespace URI. cls = _aliasToClass.get(QName.valueOf(classQName.getLocalName())); if (cls != null) return cls; // Finally convert the qualified name to a class (ignoring namespace URI). cls = Class.forName(classQName.getLocalName().toString()); 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; } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } } /** * 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(), name); } else { writer.writeAttribute(_classAttribute.getNamespaceURI(), _classAttribute.getLocalName(), name); } } else { if (qName != null) { if (qName.getNamespaceURI() == null) { writer.writeStartElement(qName.getLocalName()); } else { writer.writeStartElement(qName.getNamespaceURI(), qName.getLocalName()); } } else { writer.writeStartElement(name); } } } public void reset() { _classAttribute = QName.valueOf("class"); _aliasToClass.clear(); _classToAlias.clear(); } private static final long serialVersionUID = 6611041662550083919L; }