/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * XMLSerialization.java * Copyright (C) 2004-2012 University of Waikato, Hamilton, New Zealand * package weka.core.xml; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Vector; import org.w3c.dom.Document; import org.w3c.dom.Element; import weka.core.RevisionHandler; import weka.core.RevisionUtils; import weka.core.Utils; import weka.core.Version; *//** * With this class objects can be serialized to XML instead into a binary format. It uses introspection (cf. beans) to retrieve the data from the given object, i.e. it can only access beans-conform fields automatically. * <p> * The generic approach of writing data as XML can be overriden by adding custom methods for reading/writing in a derived class (cf. <code>m_Properties</code>, <code>m_CustomMethods</code>).<br> * Custom read and write methods must have the same signature (and also be <code>public</code>!) as the <code>readFromXML</code> and <code>writeToXML</code> methods. Methods that apply to the naming rule <code>read + property name</code> are added automatically to the list of methods by the method <code>XMLSerializationMethodHandler.addMethods(...)</code>. * <p> * Other properties that are not conform the bean set/get-methods have to be processed manually in a derived class (cf. <code>readPostProcess(Object)</code>, <code>writePostProcess(Object)</code>). * <p> * For a complete XML serialization/deserialization have a look at the <code>KOML</code> class. * <p> * If a stored class has a constructor that takes a String to initialize (e.g. String or Double) then the content of the tag will used for the constructor, e.g. from * * <pre> * <object name="name" class="String" primitive="no">Smith</object> * </pre> * * "Smith" will be used to instantiate a String object as constructor argument. * <p> * * @see KOML * @see #fromXML(Document) * @see #toXML(Object) * @see #m_Properties * @see #m_CustomMethods * @see #readPostProcess(Object) * @see #writePostProcess(Object) * @see #readFromXML(Element) * @see #writeToXML(Element, Object, String) * * @author FracPete (fracpete at waikato dot ac dot nz) * @version $Revision: 9413 $ */ /* public class XMLSerialization implements RevisionHandler { *//** for debugging purposes only */ /* protected static boolean DEBUG = false; *//** * the node that is currently processed, in case of writing the parent node (something might go wrong writing the new child) and in case of reading the actual node that is tried to process */ /* protected Element m_CurrentNode = null; *//** the tag for an object */ /* public final static String TAG_OBJECT = "object"; *//** the version attribute */ /* public final static String ATT_VERSION = XMLDocument.ATT_VERSION; *//** the tag for the name */ /* public final static String ATT_NAME = XMLDocument.ATT_NAME; *//** the tag for the class */ /* public final static String ATT_CLASS = "class"; *//** the tag whether primitive or not (yes/no) */ /* public final static String ATT_PRIMITIVE = "primitive"; *//** the tag whether array or not (yes/no) */ /* public final static String ATT_ARRAY = "array"; *//** the tag whether null or not (yes/no) */ /* public final static String ATT_NULL = "null"; *//** the value "yes" for the primitive and array attribute */ /* public final static String VAL_YES = XMLDocument.VAL_YES; *//** the value "no" for the primitive and array attribute */ /* public final static String VAL_NO = XMLDocument.VAL_NO; *//** the value of the name for the root node */ /* public final static String VAL_ROOT = "__root__"; *//** the root node of the XML document */ /* public final static String ROOT_NODE = TAG_OBJECT; *//** * default value for attribute ATT_PRIMITIVE * * @see #ATT_PRIMITIVE */ /* public final static String ATT_PRIMITIVE_DEFAULT = VAL_NO; *//** * default value for attribute ATT_ARRAY * * @see #ATT_ARRAY */ /* public final static String ATT_ARRAY_DEFAULT = VAL_NO; *//** * default value for attribute ATT_NULL * * @see #ATT_NULL */ /* public final static String ATT_NULL_DEFAULT = VAL_NO; *//** the DOCTYPE for the serialization */ /* public final static String DOCTYPE = "<!" + XMLDocument.DTD_DOCTYPE + " " + ROOT_NODE + "\n" + "[\n" + " <!" + XMLDocument.DTD_ELEMENT + " " + TAG_OBJECT + " (" + XMLDocument.DTD_PCDATA + XMLDocument.DTD_SEPARATOR + TAG_OBJECT + ")" + XMLDocument.DTD_ZERO_OR_MORE + ">\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_NAME + " " + XMLDocument.DTD_CDATA + " " + XMLDocument.DTD_REQUIRED + ">\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_CLASS + " " + XMLDocument.DTD_CDATA + " " + XMLDocument.DTD_REQUIRED + ">\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_PRIMITIVE + " " + XMLDocument.DTD_CDATA + " \"" + ATT_PRIMITIVE_DEFAULT + "\">\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_ARRAY + " " + XMLDocument.DTD_CDATA + " \"" + ATT_ARRAY_DEFAULT + "\"> <!-- the dimensions of the array; no=0, yes=1 -->\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_NULL + " " + XMLDocument.DTD_CDATA + " \"" + ATT_NULL_DEFAULT + "\">\n" + " <!" + XMLDocument.DTD_ATTLIST + " " + TAG_OBJECT + " " + ATT_VERSION + " " + XMLDocument.DTD_CDATA + " \"" + Version.VERSION + "\">\n" + "]\n" + ">"; *//** * List of fully qualified property names to suppress any warning messages for */ /* public final static List<String> SUPPRESS_PROPERTY_WARNINGS = new ArrayList<String>(); *//** the XMLDocument that performs the transformation to and fro XML */ /* protected XMLDocument m_Document = null; *//** for handling properties (ignored/allowed) */ /* protected PropertyHandler m_Properties = null; *//** for handling custom read/write methods */ /* protected XMLSerializationMethodHandler m_CustomMethods = null; *//** * for overriding class names (Class <-> Classname (String)) * * @see #overrideClassname(Object) */ /* protected Hashtable<Class, String> m_ClassnameOverride = null; *//** * initializes the serialization * * @throws Exception * if initialization fails */ /* public XMLSerialization() throws Exception { super(); clear(); } *//** * used for debugging purposes, i.e. only if DEBUG is set to true. needs a newly generated Throwable instance to get the method/line from * * @param t * a throwable instance, generated in the calling method * @param msg * a message to pring * @see #DEBUG */ /* protected void trace(Throwable t, String msg) { if ((DEBUG) && (t.getStackTrace().length > 0)) { System.out.println("trace: " + t.getStackTrace()[0] + ": " + msg); } } *//** * generates internally a new XML document and clears also the IgnoreList and the mappings for the Read/Write-Methods * * @throws Exception * if something goes wrong */ /* public void clear() throws Exception { m_Document = new XMLDocument(); m_Document.setValidating(true); m_Document.newDocument(DOCTYPE, ROOT_NODE); m_Properties = new PropertyHandler(); m_CustomMethods = new XMLSerializationMethodHandler(this); m_ClassnameOverride = new Hashtable<Class, String>(); // java.io.File is sometimes represented as another class: // - Win32: sun.awt.shell.Win32ShellFolder2 // - Linux: sun.awt.shell.DefaultShellFolder // -> we set it to "java.io.File" m_ClassnameOverride.put(java.io.File.class, java.io.File.class.getName()); setVersion(Version.VERSION); m_CurrentNode = null; } *//** * sets the given version string in the XML document * * @param version * the new version string */ /* private void setVersion(String version) { Document doc; doc = m_Document.getDocument(); doc.getDocumentElement().setAttribute(ATT_VERSION, version); } *//** * returns the WEKA version with which the serialized object was created * * @return the current version * @see Version */ /* public String getVersion() { Document doc; String result; doc = m_Document.getDocument(); result = doc.getDocumentElement().getAttribute(ATT_VERSION); return result; } *//** * Checks the version in the current Document with the one of the current release. If the version differ, a warning is printed. */ /* private void checkVersion() { String versionStr; Version version; version = new Version(); versionStr = getVersion(); if (versionStr.equals("")) System.out.println("WARNING: has no version!"); else if (version.isOlder(versionStr)) System.out.println("WARNING: loading a newer version (" + versionStr + " > " + Version.VERSION + ")!"); else if (version.isNewer(versionStr)) System.out.println("NOTE: loading an older version (" + versionStr + " < " + Version.VERSION + ")!"); } *//** * returns a hashtable with PropertyDescriptors that have "get" and "set" methods indexed by the property name. * * @see java.beans.PropertyDescriptor * @param o * the object to retrieve the descriptors from * @return the PropertyDescriptors indexed by name of the property * @throws Exception * if the introspection fails */ /* protected Hashtable getDescriptors(Object o) throws Exception { BeanInfo info; PropertyDescriptor[] desc; int i; Hashtable<String, PropertyDescriptor> result; result = new Hashtable<String, PropertyDescriptor>(); info = Introspector.getBeanInfo(o.getClass()); desc = info.getPropertyDescriptors(); for (i = 0; i < desc.length; i++) { // get AND set method? if ((desc[i].getReadMethod() != null) && (desc[i].getWriteMethod() != null)) { // in ignore list, i.e. a general ignore without complete path? if (m_Properties.isIgnored(desc[i].getDisplayName())) continue; // in ignore list of the class? if (m_Properties.isIgnored(o, desc[i].getDisplayName())) continue; // not an allowed property if (!m_Properties.isAllowed(o, desc[i].getDisplayName())) continue; result.put(desc[i].getDisplayName(), desc[i]); } } return result; } *//** * returns the path of the "name" attribute from the root down to this node (including it). * * @param node * the node to get the path for * @return the complete "name" path of this node */ /* protected String getPath(Element node) { String result; result = node.getAttribute(ATT_NAME); while (node.getParentNode() != node.getOwnerDocument()) { node = (Element) node.getParentNode(); result = node.getAttribute(ATT_NAME) + "." + result; } return result; } *//** * returns either <code>VAL_YES</code> or <code>VAL_NO</code> depending on the value of <code>b</code> * * @param b * the boolean to turn into a string * @return the value in string representation */ /* protected String booleanToString(boolean b) { if (b) return VAL_YES; else return VAL_NO; } *//** * turns the given string into a boolean, if a positive number is given, then zero is considered FALSE, every other number TRUE; the empty string is also considered being FALSE * * @param s * the string to turn into a boolean * @return the string as boolean */ /* protected boolean stringToBoolean(String s) { if (s.equals("")) return false; else if (s.equals(VAL_YES)) return true; else if (s.equalsIgnoreCase("true")) return true; else if (s.replaceAll("[0-9]*", "").equals("")) return (Integer.parseInt(s) != 0); else return false; } *//** * appends a new node to the parent with the given parameters (a non-array) * * @param parent * the parent of this node. if it is <code>null</code> the document root element is used * @param name * the name of the node * @param classname * the classname for this node * @param primitive * whether it is a primitve data type or not (i.e. an object) * @return the generated node */ /* protected Element addElement(Element parent, String name, String classname, boolean primitive) { return addElement(parent, name, classname, primitive, 0); } *//** * appends a new node to the parent with the given parameters * * @param parent * the parent of this node. if it is <code>null</code> the document root element is used * @param name * the name of the node * @param classname * the classname for this node * @param primitive * whether it is a primitve data type or not (i.e. an object) * @param array * the dimensions of the array (0 if not an array) * @return the generated node */ /* protected Element addElement(Element parent, String name, String classname, boolean primitive, int array) { return addElement(parent, name, classname, primitive, array, false); } *//** * appends a new node to the parent with the given parameters * * @param parent * the parent of this node. if it is <code>null</code> the document root element is used * @param name * the name of the node * @param classname * the classname for this node * @param primitive * whether it is a primitve data type or not (i.e. an object) * @param array * the dimensions of the array (0 if not an array) * @param isnull * whether it is null * @return the generated node */ /* protected Element addElement(Element parent, String name, String classname, boolean primitive, int array, boolean isnull) { Element result; if (parent == null) result = m_Document.getDocument().getDocumentElement(); else result = (Element) parent.appendChild(m_Document.getDocument().createElement(TAG_OBJECT)); // attributes // mandatory attributes: result.setAttribute(ATT_NAME, name); result.setAttribute(ATT_CLASS, classname); // add following attributes only if necessary, i.e., different from default: if (!booleanToString(primitive).equals(ATT_PRIMITIVE_DEFAULT)) result.setAttribute(ATT_PRIMITIVE, booleanToString(primitive)); // multi-dimensional array? if (array > 1) { result.setAttribute(ATT_ARRAY, Integer.toString(array)); } // backwards compatible: 0 -> no array ("no"), 1 -> 1-dim. array ("yes") else { if (!booleanToString(array == 1).equals(ATT_ARRAY_DEFAULT)) result.setAttribute(ATT_ARRAY, booleanToString(array == 1)); } if (!booleanToString(isnull).equals(ATT_NULL_DEFAULT)) result.setAttribute(ATT_NULL, booleanToString(isnull)); return result; } *//** * if the class of the given object (or one of its ancestors) is stored in the classname override hashtable, then the override name is returned otherwise the classname of the given object. * * @param o * the object to check for overriding its classname * @return if overridden then the classname stored in the hashtable, otherwise the classname of the given object * @see #m_ClassnameOverride */ /* protected String overrideClassname(Object o) { Enumeration enm; String result; Class currentCls; result = o.getClass().getName(); // check overrides enm = m_ClassnameOverride.keys(); while (enm.hasMoreElements()) { currentCls = (Class) enm.nextElement(); if (currentCls.isInstance(o)) { result = m_ClassnameOverride.get(currentCls); break; } } return result; } *//** * if the given classname is stored in the classname override hashtable, then the override name is returned otherwise the given classname. <b>Note:</b> in contrast to <code>overrideClassname(Object)</code> does this method only look for exact name matches. The other method checks whether the class of the given object is a subclass of any of the stored overrides. * * @param classname * the classname to check for overriding * @return if overridden then the classname stored in the hashtable, otherwise the given classname * @see #m_ClassnameOverride * @see #overrideClassname(Object) */ /* protected String overrideClassname(String classname) { Enumeration enm; String result; Class currentCls; result = classname; // check overrides enm = m_ClassnameOverride.keys(); while (enm.hasMoreElements()) { currentCls = (Class) enm.nextElement(); if (currentCls.getName().equals(classname)) { result = m_ClassnameOverride.get(currentCls); break; } } return result; } *//** * returns a property descriptor if possible, otherwise <code>null</code> * * @param className * the name of the class to get the descriptor for * @param displayName * the name of the property * @return the descriptor if available, otherwise <code>null</code> */ /* protected PropertyDescriptor determineDescriptor(String className, String displayName) { PropertyDescriptor result; result = null; try { result = new PropertyDescriptor(displayName, Class.forName(className)); } catch (Exception e) { result = null; } return result; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeBooleanToXML(Element parent, boolean o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Boolean.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Boolean(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeByteToXML(Element parent, byte o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Byte.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Byte(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeCharToXML(Element parent, char o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Character.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Character(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeDoubleToXML(Element parent, double o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Double.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Double(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeFloatToXML(Element parent, float o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Float.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Float(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeIntToXML(Element parent, int o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Integer.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Integer(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeLongToXML(Element parent, long o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Long.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Long(o).toString())); return node; } *//** * adds the given primitive to the DOM structure. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the primitive to describe in XML * @param name * the name of the primitive * @return the node that was created * @throws Exception * if the DOM creation fails */ /* protected Element writeShortToXML(Element parent, short o, String name) throws Exception { Element node; // for debugging only if (DEBUG) trace(new Throwable(), name); m_CurrentNode = parent; node = addElement(parent, name, Short.TYPE.getName(), true); node.appendChild(node.getOwnerDocument().createTextNode(new Short(o).toString())); return node; } *//** * checks whether the innermost class is a primitive class (handles multi-dimensional arrays) * * @param c * the array class to inspect * @return whether the array consists of primitive elements */ /* protected boolean isPrimitiveArray(Class c) { if (c.getComponentType().isArray()) return isPrimitiveArray(c.getComponentType()); else return c.getComponentType().isPrimitive(); } *//** * adds the given Object to a DOM structure. (only public due to reflection).<br> * <b>Note:</b> <code>overrideClassname(Object)</code> is not invoked in case of arrays, since the array class could be a superclass, whereas the elements of the array can be specialized subclasses. In case of an array the method <code>overrideClassname(String)</code> is invoked, which searches for an exact match of the classname in the override hashtable. * * @param parent * the parent of this object, e.g. the class this object is a member of * @param o * the Object to describe in XML * @param name * the name of the object * @return the node that was created * @throws Exception * if the DOM creation fails * @see #overrideClassname(Object) * @see #overrideClassname(String) * @see #m_ClassnameOverride */ /* public Element writeToXML(Element parent, Object o, String name) throws Exception { String classname; Element node; Hashtable memberlist; Enumeration enm; Object member; String memberName; Method method; PropertyDescriptor desc; boolean primitive; int array; int i; Object obj; String tmpStr; node = null; // for debugging only if (DEBUG) trace(new Throwable(), name); // special handling of null-objects if (o == null) { node = addElement(parent, name, "" + null, false, 0, true); return node; } // used for overriding the classname obj = null; // get information about object array = 0; if (o.getClass().isArray()) array = Utils.getArrayDimensions(o); if (array > 0) { classname = Utils.getArrayClass(o.getClass()).getName(); primitive = isPrimitiveArray(o.getClass()); } else { // try to get property descriptor to determine real class // (for primitives the getClass() method returns the corresponding // Object-Class!) desc = null; if (parent != null) desc = determineDescriptor(parent.getAttribute(ATT_CLASS), name); if (desc != null) primitive = desc.getPropertyType().isPrimitive(); else primitive = o.getClass().isPrimitive(); // for primitives: retrieve primitive type, otherwise the object's real // class. For non-primitives we can't use the descriptor, since that // might only return an interface as class! if (primitive) { classname = desc.getPropertyType().getName(); } else { obj = o; classname = o.getClass().getName(); } } // fix class/primitive if parent is array of primitives, thanks to // reflection the elements of the array are objects and not primitives! if ((parent != null) && (!parent.getAttribute(ATT_ARRAY).equals("")) && (!parent.getAttribute(ATT_ARRAY).equals(VAL_NO)) && (stringToBoolean(parent.getAttribute(ATT_PRIMITIVE)))) { primitive = true; classname = parent.getAttribute(ATT_CLASS); obj = null; } // perhaps we need to override the classname if (obj != null) classname = overrideClassname(obj); // for non-arrays else classname = overrideClassname(classname); // for arrays // create node for current object node = addElement(parent, name, classname, primitive, array); // array? -> save as child with 'name="<index>"' if (array > 0) { for (i = 0; i < Array.getLength(o); i++) { invokeWriteToXML(node, Array.get(o, i), Integer.toString(i)); } } // non-array else { // primitive? -> only toString() if (primitive) { node.appendChild(node.getOwnerDocument().createTextNode(o.toString())); } // object else { // process recursively members of this object memberlist = getDescriptors(o); // if no get/set methods -> we assume it has String-Constructor if (memberlist.size() == 0) { if (!o.toString().equals("")) { tmpStr = o.toString(); // these five entities are recognized by every XML processor // see http://www.xml.com/pub/a/2001/03/14/trxml10.html tmpStr = tmpStr.replaceAll("&", "&").replaceAll("\"", """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">"); // in addition, replace some other entities as well tmpStr = tmpStr.replaceAll("\n", " ").replaceAll("\r", " ").replaceAll("\t", " "); if (o instanceof java.io.File) { // hack to force separators to be always saved as / tmpStr = tmpStr.replace('\\', '/'); } node.appendChild(node.getOwnerDocument().createTextNode(tmpStr)); } } else { enm = memberlist.keys(); while (enm.hasMoreElements()) { memberName = enm.nextElement().toString(); // in ignore list? if ((m_Properties.isIgnored(memberName)) || (m_Properties.isIgnored(getPath(node) + "." + memberName)) || (m_Properties.isIgnored(o, getPath(node) + "." + memberName))) continue; // is it allowed? if (!m_Properties.isAllowed(o, memberName)) continue; desc = (PropertyDescriptor) memberlist.get(memberName); method = desc.getReadMethod(); member = method.invoke(o, (Object[]) null); invokeWriteToXML(node, member, memberName); } } } } return node; } *//** * either invokes a custom method to write a specific property/class or the standard method <code>writeToXML(Element,Object,String)</code> * * @param parent * the parent XML node * @param o * the object's content will be added as children to the given parent node * @param name * the name of the object * @return the node that was created * @throws Exception * if invocation or turning into XML fails */ /* protected Element invokeWriteToXML(Element parent, Object o, String name) throws Exception { Method method; Class[] methodClasses; Object[] methodArgs; boolean array; Element node; boolean useDefault; node = null; method = null; useDefault = false; m_CurrentNode = parent; // default, if null if (o == null) useDefault = true; try { if (!useDefault) { array = o.getClass().isArray(); // display name? if (m_CustomMethods.write().contains(name)) method = m_CustomMethods.write().get(o.getClass()); else // class? if ((!array) && (m_CustomMethods.write().contains(o.getClass()))) method = m_CustomMethods.write().get(o.getClass()); else method = null; useDefault = (method == null); } // custom if (!useDefault) { methodClasses = new Class[3]; methodClasses[0] = Element.class; methodClasses[1] = Object.class; methodClasses[2] = String.class; methodArgs = new Object[3]; methodArgs[0] = parent; methodArgs[1] = o; methodArgs[2] = name; node = (Element) method.invoke(this, methodArgs); } // standard else { node = writeToXML(parent, o, name); } } catch (Exception e) { if (DEBUG) e.printStackTrace(); if (m_CurrentNode != null) { System.out.println("Happened near: " + getPath(m_CurrentNode)); // print it only once! m_CurrentNode = null; } System.out.println("PROBLEM (write): " + name); throw (Exception) e.fillInStackTrace(); } return node; } *//** * enables derived classes to due some pre-processing on the objects, that's about to be serialized. Right now it only returns the object. * * @param o * the object that is serialized into XML * @return the possibly altered object * @throws Exception * if post-processing fails */ /* protected Object writePreProcess(Object o) throws Exception { return o; } *//** * enables derived classes to add other properties to the DOM tree, e.g. ones that do not apply to the get/set convention of beans. only implemented with empty method body. * * @param o * the object that is serialized into XML * @throws Exception * if post-processing fails */ /* protected void writePostProcess(Object o) throws Exception { } *//** * extracts all accesible properties from the given object * * @param o * the object to turn into an XML representation * @return the generated DOM document * @throws Exception * if XML generation fails */ /* public XMLDocument toXML(Object o) throws Exception { clear(); invokeWriteToXML(null, writePreProcess(o), VAL_ROOT); writePostProcess(o); return m_Document; } *//** * returns a descriptor for a given objet by providing the name * * @param o * the object the get the descriptor for * @param name * the display name of the descriptor * @return the Descriptor, if found, otherwise <code>null</code> * @throws Exception * if introsepction fails */ /* protected PropertyDescriptor getDescriptorByName(Object o, String name) throws Exception { PropertyDescriptor result; PropertyDescriptor[] desc; int i; result = null; desc = Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors(); for (i = 0; i < desc.length; i++) { if (desc[i].getDisplayName().equals(name)) { result = desc[i]; break; } } return result; } *//** * returns the associated class for the given name * * @param name * the name of the class to return a Class object for * @return the class if it could be retrieved * @throws Exception * if it class retrieval fails */ /* protected Class determineClass(String name) throws Exception { Class result; if (name.equals(Boolean.TYPE.getName())) result = Boolean.TYPE; else if (name.equals(Byte.TYPE.getName())) result = Byte.TYPE; else if (name.equals(Character.TYPE.getName())) result = Character.TYPE; else if (name.equals(Double.TYPE.getName())) result = Double.TYPE; else if (name.equals(Float.TYPE.getName())) result = Float.TYPE; else if (name.equals(Integer.TYPE.getName())) result = Integer.TYPE; else if (name.equals(Long.TYPE.getName())) result = Long.TYPE; else if (name.equals(Short.TYPE.getName())) result = Short.TYPE; else result = Class.forName(name); return result; } *//** * returns an Object representing the primitive described by the given node. Here we use a trick to return an object even though its a primitive: by creating a primitive array with reflection of length 1, setting the primtive value as real object and then returning the "object" at position 1 of the array. * * @param node * the node to return the value as "primitive" object * @return the primitive as "pseudo" object * @throws Exception * if the instantiation of the array fails or any of the String conversions fails */ /* protected Object getPrimitive(Element node) throws Exception { Object result; Object tmpResult; Class cls; cls = determineClass(node.getAttribute(ATT_CLASS)); tmpResult = Array.newInstance(cls, 1); if (cls == Boolean.TYPE) Array.set(tmpResult, 0, new Boolean(XMLDocument.getContent(node))); else if (cls == Byte.TYPE) Array.set(tmpResult, 0, new Byte(XMLDocument.getContent(node))); else if (cls == Character.TYPE) Array.set(tmpResult, 0, new Character(XMLDocument.getContent(node).charAt(0))); else if (cls == Double.TYPE) Array.set(tmpResult, 0, new Double(XMLDocument.getContent(node))); else if (cls == Float.TYPE) Array.set(tmpResult, 0, new Float(XMLDocument.getContent(node))); else if (cls == Integer.TYPE) Array.set(tmpResult, 0, new Integer(XMLDocument.getContent(node))); else if (cls == Long.TYPE) Array.set(tmpResult, 0, new Long(XMLDocument.getContent(node))); else if (cls == Short.TYPE) Array.set(tmpResult, 0, new Short(XMLDocument.getContent(node))); else throw new Exception("Cannot get primitive for class '" + cls.getName() + "'!"); result = Array.get(tmpResult, 0); return result; } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public boolean readBooleanFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Boolean) getPrimitive(node)).booleanValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public byte readByteFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Byte) getPrimitive(node)).byteValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public char readCharFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Character) getPrimitive(node)).charValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public double readDoubleFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Double) getPrimitive(node)).doubleValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public float readFloatFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Float) getPrimitive(node)).floatValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public int readIntFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Integer) getPrimitive(node)).intValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public long readLongFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Long) getPrimitive(node)).longValue(); } *//** * builds the primitive from the given DOM node. * * @param node * the associated XML node * @return the primitive created from the XML description * @throws Exception * if instantiation fails */ /* public short readShortFromXML(Element node) throws Exception { // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; return ((Short) getPrimitive(node)).shortValue(); } *//** * adds the specific node to the object via a set method * * @param o * the object to set a property * @param name * the name of the object for which to set a property (only for information reasons) * @param child * the value of the property to add * @return the provided object, but augmented by the child * @throws Exception * if something goes wrong */ /* public Object readFromXML(Object o, String name, Element child) throws Exception { Object result; Hashtable descriptors; PropertyDescriptor descriptor; String methodName; Method method; Object[] methodArgs; Object tmpResult; Class paramClass; result = o; descriptors = getDescriptors(result); methodName = child.getAttribute(ATT_NAME); // in ignore list? if (m_Properties.isIgnored(getPath(child))) return result; // in ignore list of class? if (m_Properties.isIgnored(result, getPath(child))) return result; // is it allowed? if (!m_Properties.isAllowed(result, methodName)) return result; descriptor = (PropertyDescriptor) descriptors.get(methodName); // unknown property? if (descriptor == null) { if (!m_CustomMethods.read().contains(methodName) && !SUPPRESS_PROPERTY_WARNINGS.contains(name + "." + methodName)) System.out.println("WARNING: unknown property '" + name + "." + methodName + "'!"); return result; } method = descriptor.getWriteMethod(); methodArgs = new Object[1]; tmpResult = invokeReadFromXML(child); paramClass = method.getParameterTypes()[0]; // array? if (paramClass.isArray()) { // no data? if (Array.getLength(tmpResult) == 0) return result; methodArgs[0] = tmpResult; } // non-array else { methodArgs[0] = tmpResult; } method.invoke(result, methodArgs); return result; } *//** * returns an array with the dimensions of the array stored in XML * * @param node * the node to determine the dimensions for * @return the dimensions of the array */ /* protected int[] getArrayDimensions(Element node) { Vector<Element> children; Vector<Integer> tmpVector; int[] tmp; int[] result; int i; // have we reached the innermost dimension? if (stringToBoolean(node.getAttribute(ATT_ARRAY))) children = XMLDocument.getChildTags(node); else children = null; if (children != null) { tmpVector = new Vector<Integer>(); if (children.size() > 0) { // are children also arrays? tmp = getArrayDimensions(children.get(0)); // further dimensions if (tmp != null) { for (i = tmp.length - 1; i >= 0; i--) tmpVector.add(new Integer(tmp[i])); } // add current dimension tmpVector.add(0, new Integer(children.size())); } else { tmpVector.add(new Integer(0)); } // generate result result = new int[tmpVector.size()]; for (i = 0; i < result.length; i++) result[i] = tmpVector.get(tmpVector.size() - i - 1).intValue(); } else { result = null; } return result; } *//** * builds the object from the given DOM node. (only public due to reflection) * * @param node * the associated XML node * @return the instance created from the XML description * @throws Exception * if instantiation fails */ /* public Object readFromXML(Element node) throws Exception { String classname; String name; boolean primitive; boolean array; boolean isnull; Class<?> cls; Vector<Element> children; Object result; int i; Constructor constructor; Class[] methodClasses; Object[] methodArgs; Element child; // for debugging only if (DEBUG) trace(new Throwable(), node.getAttribute(ATT_NAME)); m_CurrentNode = node; result = null; name = node.getAttribute(ATT_NAME); classname = node.getAttribute(ATT_CLASS); primitive = stringToBoolean(node.getAttribute(ATT_PRIMITIVE)); array = stringToBoolean(node.getAttribute(ATT_ARRAY)); isnull = stringToBoolean(node.getAttribute(ATT_NULL)); // special handling of null if (isnull) return result; children = XMLDocument.getChildTags(node); cls = determineClass(classname); // array if (array) { result = Array.newInstance(cls, getArrayDimensions(node)); for (i = 0; i < children.size(); i++) { child = children.get(i); Array.set(result, Integer.parseInt(child.getAttribute(ATT_NAME)), invokeReadFromXML(child)); } } // non-array else { // primitive/String-constructor if (children.size() == 0) { // primitive if (primitive) { result = getPrimitive(node); } // assumed String-constructor else { methodClasses = new Class[1]; methodClasses[0] = String.class; methodArgs = new Object[1]; methodArgs[0] = XMLDocument.getContent(node); try { constructor = cls.getConstructor(methodClasses); result = constructor.newInstance(methodArgs); } catch (Exception e) { // if it's not a class with String constructor, let's try standard // constructor try { result = cls.newInstance(); } catch (Exception e2) { // sorry, can't instantiate! result = null; System.out.println("ERROR: Can't instantiate '" + classname + "'!"); } } } } // normal get/set methods else { result = cls.newInstance(); for (i = 0; i < children.size(); i++) result = readFromXML(result, name, children.get(i)); } } return result; } *//** * either invokes a custom method to read a specific property/class or the standard method <code>readFromXML(Element)</code> * * @param node * the associated XML node * @return the instance created from the XML description * @throws Exception * if instantiation fails */ /* protected Object invokeReadFromXML(Element node) throws Exception { Method method; Class[] methodClasses; Object[] methodArgs; boolean array; boolean useDefault; useDefault = false; method = null; m_CurrentNode = node; try { // special handling of null values if (stringToBoolean(node.getAttribute(ATT_NULL))) useDefault = true; if (!useDefault) { array = stringToBoolean(node.getAttribute(ATT_ARRAY)); // display name? if (m_CustomMethods.read().contains(node.getAttribute(ATT_NAME))) method = m_CustomMethods.read().get(node.getAttribute(ATT_NAME)); else // class name? if ((!array) && (m_CustomMethods.read().contains(determineClass(node.getAttribute(ATT_CLASS))))) method = m_CustomMethods.read().get(determineClass(node.getAttribute(ATT_CLASS))); else method = null; useDefault = (method == null); } // custom method if (!useDefault) { methodClasses = new Class[1]; methodClasses[0] = Element.class; methodArgs = new Object[1]; methodArgs[0] = node; return method.invoke(this, methodArgs); } // standard else { return readFromXML(node); } } catch (Exception e) { if (DEBUG) e.printStackTrace(); if (m_CurrentNode != null) { System.out.println("Happened near: " + getPath(m_CurrentNode)); // print it only once! m_CurrentNode = null; } System.out.println("PROBLEM (read): " + node.getAttribute("name")); throw (Exception) e.fillInStackTrace(); } } *//** * additional pre-processing can happen in derived classes before the actual reading from XML (working on the raw XML). right now it does nothing with the document. * * @param document * the document to pre-process * @return the processed object * @throws Exception * if post-processing fails */ /* protected Document readPreProcess(Document document) throws Exception { return document; } *//** * additional post-processing can happen in derived classes after reading from XML. right now it only returns the object as it is. * * @param o * the object to perform some additional processing on * @return the processed object * @throws Exception * if post-processing fails */ /* protected Object readPostProcess(Object o) throws Exception { return o; } *//** * returns the given DOM document as an instance of the specified class * * @param document * the parsed DOM document representing the object * @return the XML as object * @throws Exception * if object instantiation fails */ /* public Object fromXML(Document document) throws Exception { if (!document.getDocumentElement().getNodeName().equals(ROOT_NODE)) throw new Exception("Expected '" + ROOT_NODE + "' as root element, but found '" + document.getDocumentElement().getNodeName() + "'!"); m_Document.setDocument(readPreProcess(document)); checkVersion(); return readPostProcess(invokeReadFromXML(m_Document.getDocument().getDocumentElement())); } *//** * parses the given XML string (can be XML or a filename) and returns an Object generated from the representation * * @param xml * the xml to parse (if "<?xml" is not found then it is considered a file) * @return the generated instance * @throws Exception * if something goes wrong with the parsing */ /* public Object read(String xml) throws Exception { return fromXML(m_Document.read(xml)); } *//** * parses the given file and returns a DOM document * * @param file * the XML file to parse * @return the parsed DOM document * @throws Exception * if something goes wrong with the parsing */ /* public Object read(File file) throws Exception { return fromXML(m_Document.read(file)); } *//** * parses the given stream and returns a DOM document * * @param stream * the XML stream to parse * @return the parsed DOM document * @throws Exception * if something goes wrong with the parsing */ /* public Object read(InputStream stream) throws Exception { return fromXML(m_Document.read(stream)); } *//** * parses the given reader and returns a DOM document * * @param reader * the XML reader to parse * @return the parsed DOM document * @throws Exception * if something goes wrong with the parsing */ /* public Object read(Reader reader) throws Exception { return fromXML(m_Document.read(reader)); } *//** * writes the given object into the file * * @param file * the filename to write to * @param o * the object to serialize as XML * @throws Exception * if something goes wrong with the parsing */ /* public void write(String file, Object o) throws Exception { toXML(o).write(file); } *//** * writes the given object into the file * * @param file * the filename to write to * @param o * the object to serialize as XML * @throws Exception * if something goes wrong with the parsing */ /* public void write(File file, Object o) throws Exception { toXML(o).write(file); } *//** * writes the given object into the stream * * @param stream * the filename to write to * @param o * the object to serialize as XML * @throws Exception * if something goes wrong with the parsing */ /* public void write(OutputStream stream, Object o) throws Exception { toXML(o).write(stream); } *//** * writes the given object into the writer * * @param writer * the filename to write to * @param o * the object to serialize as XML * @throws Exception * if something goes wrong with the parsing */ /* public void write(Writer writer, Object o) throws Exception { toXML(o).write(writer); } *//** * for testing only. if the first argument is a filename with ".xml" as extension it tries to generate an instance from the XML description and does a <code>toString()</code> of the generated object. */ /* public static void main(String[] args) throws Exception { if (args.length > 0) { // read xml and print if (args[0].toLowerCase().endsWith(".xml")) { System.out.println(new XMLSerialization().read(args[0]).toString()); } // read binary and print generated XML else { // read FileInputStream fi = new FileInputStream(args[0]); ObjectInputStream oi = new ObjectInputStream(new BufferedInputStream(fi)); Object o = oi.readObject(); oi.close(); // print to stdout // new XMLSerialization().write(System.out, o); new XMLSerialization().write(new BufferedOutputStream(new FileOutputStream(args[0] + ".xml")), o); // print to binary file FileOutputStream fo = new FileOutputStream(args[0] + ".exp"); ObjectOutputStream oo = new ObjectOutputStream(new BufferedOutputStream(fo)); oo.writeObject(o); oo.close(); } } } *//** * Returns the revision string. * * @return the revision */ /* @Override public String getRevision() { return RevisionUtils.extract("$Revision: 9413 $"); } } */