You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/05/18 16:43:24 UTC
svn commit: r775969 - in /incubator/pivot/trunk/wtk/src/pivot/wtkx:
Bindable.java WTKXCompiler.java WTKXSerializer.java
Author: tvolkert
Date: Mon May 18 14:43:24 2009
New Revision: 775969
URL: http://svn.apache.org/viewvc?rev=775969&view=rev
Log:
Added initial take at WTKXCompiler
Added:
incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXCompiler.java
Modified:
incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java
incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java
Modified: incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java?rev=775969&r1=775968&r2=775969&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java Mon May 18 14:43:24 2009
@@ -178,6 +178,17 @@
}
/**
+ * Interface denoting a WTKX object hierarchy to which we can bind values.
+ *
+ * @author tvolkert
+ */
+ public static interface ObjectHierarchy {
+ <T> T getRootObject();
+
+ <T> T getObjectByID(String id);
+ }
+
+ /**
* Creates a new <tt>Bindable</tt> object.
*/
protected Bindable() {
Added: incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXCompiler.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXCompiler.java?rev=775969&view=auto
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXCompiler.java (added)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXCompiler.java Mon May 18 14:43:24 2009
@@ -0,0 +1,662 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtkx;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.nio.CharBuffer;
+
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.JavaFileObject;
+import javax.tools.OptionChecker;
+import javax.tools.Tool;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamException;
+
+import pivot.beans.BeanDictionary;
+import pivot.collections.ArrayList;
+import pivot.collections.Dictionary;
+import pivot.collections.List;
+import pivot.collections.Sequence;
+import pivot.serialization.Serializer;
+import pivot.serialization.SerializationException;
+import pivot.util.ListenerList;
+
+/**
+ * Loads an object hierarchy from an XML document into compilable file objects.
+ *
+ * @author tvolkert
+ */
+public class WTKXCompiler implements Tool, OptionChecker {
+ /**
+ * A generated Java file object.
+ *
+ * @author tvolkert
+ */
+ private static class JavaFile implements JavaFileObject {
+ private final File file;
+
+ public JavaFile() {
+ try {
+ file = File.createTempFile(WTKX_PREFIX, JAVA_SUFFIX);
+ } catch (IOException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+
+ public URI toUri() {
+ return file.toURI();
+ }
+
+ public String getName() {
+ return file.getPath();
+ }
+
+ public InputStream openInputStream() throws IOException {
+ return new FileInputStream(file);
+ }
+
+ public OutputStream openOutputStream() throws IOException {
+ return new FileOutputStream(file);
+ }
+
+ public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+ return new FileReader(file);
+ }
+
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ String result;
+
+ Reader reader = openReader(ignoreEncodingErrors);
+ try {
+ char[] buf = new char[(int)file.length()];
+ int n = reader.read(buf, 0, buf.length);
+ result = new String(buf, 0, n);
+ } finally {
+ reader.close();
+ }
+
+ return result;
+ }
+
+ public Writer openWriter() throws IOException {
+ return new FileWriter(file);
+ }
+
+ public long getLastModified() {
+ return file.lastModified();
+ }
+
+ public boolean delete() {
+ return file.delete();
+ }
+
+ public Kind getKind() {
+ return Kind.SOURCE;
+ }
+
+ public boolean isNameCompatible(String simpleName, Kind kind) {
+ return (kind == Kind.SOURCE);
+ }
+
+ public NestingKind getNestingKind() {
+ return NestingKind.TOP_LEVEL;
+ }
+
+ public Modifier getAccessLevel() {
+ return Modifier.PUBLIC;
+ }
+ }
+
+ /**
+ * @author gbrown
+ */
+ private static class Element {
+ public enum Type {
+ INSTANCE,
+ INCLUDE,
+ READ_ONLY_PROPERTY,
+ WRITABLE_PROPERTY
+ }
+
+ public final Element parent;
+ public final Type type;
+ public final List<Attribute> attributes;
+
+ public Class<?> clazz;
+ public int ref;
+
+ public static int counter = -1;
+
+ public Element(Element parent, Type type, List<Attribute> attributes, Class<?> clazz, int ref) {
+ this.parent = parent;
+ this.type = type;
+ this.attributes = attributes;
+ this.clazz = clazz;
+ this.ref = ref;
+ }
+ }
+
+ /**
+ * @author gbrown
+ */
+ private static class Attribute {
+ public final String namespaceURI;
+ public final String prefix;
+ public final String localName;
+ public final String value;
+
+ public Attribute(String namespaceURI, String prefix, String localName, String value) {
+ this.namespaceURI = namespaceURI;
+ this.prefix = prefix;
+ this.localName = localName;
+ this.value = value;
+ }
+ }
+
+ private XMLInputFactory xmlInputFactory;
+
+ private static final String WTKX_PREFIX = "WTKX";
+ private static final String JAVA_SUFFIX = ".java";
+
+ private static final String SPACE = " ";
+
+ public WTKXCompiler() {
+ xmlInputFactory = XMLInputFactory.newInstance();
+ xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true);
+ }
+
+ public JavaFileObject readObject(URL location) throws IOException,
+ SerializationException {
+ if (location == null) {
+ throw new IllegalArgumentException("location is null.");
+ }
+
+ InputStream inputStream = new BufferedInputStream(location.openStream());
+ try {
+ return readObject(inputStream);
+ } finally {
+ inputStream.close();
+ }
+ }
+
+ /**
+ * Reads WTKX input stream into a compilable file object. The file object
+ * will hold a class that implements the {@link Bindable.ObjectHierarchy}
+ * interface.
+ *
+ * @param inputStream
+ * The data stream from which the WTKX will be read
+ *
+ * @return
+ * The compilable java file object represented by the WTKX
+ */
+ public JavaFileObject readObject(InputStream inputStream) throws IOException,
+ SerializationException {
+ if (inputStream == null) {
+ throw new IllegalArgumentException("inputStream is null.");
+ }
+
+ JavaFile javaFile = new JavaFile();
+
+ Writer writer = javaFile.openWriter();
+ try {
+ String[] path = javaFile.getName().split(File.separator);
+ String tmpClassName = path[path.length - 1].split("\\.")[0];
+ tmpClassName = "Foo";
+
+ writer.write(String.format(
+ "package pivot.wtkx.test;\n" +
+ "\n" +
+ "import pivot.collections.HashMap;\n" +
+ "import pivot.wtkx.Bindable;\n" +
+ "\n" +
+ "public class %2$s implements Bindable.ObjectHierarchy {\n" +
+ "%1$4sprivate HashMap<String, Object> namedObjects = new HashMap<String, Object>();\n" +
+ "\n" +
+ "%1$4s@SuppressWarnings(\"unchecked\")\n" +
+ "%1$4spublic <T> T getObjectByID(String id) {\n" +
+ "%1$8sObject object = namedObjects.get(id);\n" +
+ "%1$8sreturn (T)object;\n" +
+ "%1$4s}\n" +
+ "\n" +
+ "%1$4s@SuppressWarnings({\"unchecked\", \"cast\"})\n" +
+ "%1$4spublic <T> T getRootObject() {\n" +
+ "%1$8sObject result = null;\n",
+ SPACE, tmpClassName));
+
+ // Parse the XML stream
+ Element element = null;
+ try {
+ XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(inputStream);
+
+ while (reader.hasNext()) {
+ int event = reader.next();
+
+ switch (event) {
+ case XMLStreamConstants.CHARACTERS: {
+ // TODO
+ break;
+ }
+
+ case XMLStreamConstants.START_ELEMENT: {
+ String namespaceURI = reader.getNamespaceURI();
+ String prefix = reader.getPrefix();
+ String localName = reader.getLocalName();
+
+ String id = null;
+
+ if (prefix != null
+ && prefix.equals(WTKXSerializer.WTKX_PREFIX)) {
+ if (element == null) {
+ throw new SerializationException(prefix + ":" + localName
+ + " is not a valid root element.");
+ }
+
+ if (localName.equals(WTKXSerializer.INCLUDE_TAG)) {
+ // TODO
+ throw new SerializationException(prefix + ":" + localName
+ + " compilation is not yet implemented.");
+ } else if (localName.equals(WTKXSerializer.SCRIPT_TAG)) {
+ throw new SerializationException(prefix + ":" + localName
+ + " tags may not be compiled.");
+ } else {
+ throw new SerializationException(prefix + ":" + localName
+ + " is not a valid tag.");
+ }
+ } else {
+ ArrayList<Attribute> attributes = new ArrayList<Attribute>();
+
+ for (int i = 0, n = reader.getAttributeCount(); i < n; i++) {
+ String attributeNamespaceURI = reader.getAttributeNamespace(i);
+ if (attributeNamespaceURI == null) {
+ attributeNamespaceURI = reader.getNamespaceURI("");
+ }
+
+ String attributePrefix = reader.getAttributePrefix(i);
+ String attributeLocalName = reader.getAttributeLocalName(i);
+ String attributeValue = reader.getAttributeValue(i);
+
+ if (attributePrefix != null
+ && attributePrefix.equals(WTKXSerializer.WTKX_PREFIX)) {
+ if (attributeLocalName.equals(WTKXSerializer.ID_ATTRIBUTE)) {
+ id = attributeValue;
+ }
+ } else {
+ attributes.add(new Attribute(attributeNamespaceURI,
+ attributePrefix, attributeLocalName, attributeValue));
+ }
+ }
+
+ if (Character.isUpperCase(localName.charAt(0))) {
+ // The element represents a typed object
+ if (namespaceURI == null) {
+ throw new SerializationException("No XML namespace specified for "
+ + localName + " tag.");
+ }
+
+ String className = namespaceURI + "." + localName;
+ writer.write(String.format
+ ("%8s%s o%d = new %s();\n", SPACE, className, ++Element.counter, className));
+
+ try {
+ className = namespaceURI + "." + localName.replace('.', '$');
+ Class<?> type = Class.forName(className, false, getClass().getClassLoader());
+ element = new Element(element, Element.Type.INSTANCE,
+ attributes, type, Element.counter);
+ } catch(Exception exception) {
+ throw new SerializationException(exception);
+ }
+ } else {
+ // The element represents a property
+ if (element == null) {
+ throw new SerializationException("Root node must represent a typed object.");
+ }
+
+ if (element.type != Element.Type.INSTANCE) {
+ throw new SerializationException("Property elements must apply to typed objects.");
+ }
+
+ Class<?> type = (Class<?>)element.clazz;
+
+ if (BeanDictionary.isReadOnly(type, localName)) {
+ Class<?> valueType = BeanDictionary.getType(type, localName);
+ Method getterMethod = BeanDictionary.getGetterMethod(type, localName);
+
+ // Instantiate the property so we have a reference to it
+ writer.write(String.format
+ ("%8s%s o%d = o%d.%s();\n", SPACE, valueType.getName().replace('$', '.'),
+ ++Element.counter, element.ref, getterMethod.getName()));
+ writer.write(String.format
+ ("%8sassert (o%d != null) : \"Read-only properties cannot be null.\";\n",
+ SPACE, Element.counter));
+
+ element = new Element(element, Element.Type.READ_ONLY_PROPERTY,
+ attributes, valueType, Element.counter);
+ } else {
+ if (attributes.getLength() > 0) {
+ throw new SerializationException
+ ("Writable property elements cannot have attributes.");
+ }
+
+ element = new Element(element, Element.Type.WRITABLE_PROPERTY,
+ null, null, -1);
+ }
+ }
+ }
+
+ switch (element.type) {
+ case INCLUDE:
+ case INSTANCE: {
+ // If the element's parent is a sequence or a listener list, add
+ // the element value to it
+ if (element.parent != null) {
+ Class<?> parentType = (Class<?>)element.parent.clazz;
+
+ if (parentType != null
+ && (Sequence.class.isAssignableFrom(parentType)
+ || ListenerList.class.isAssignableFrom(parentType))) {
+ writer.write(String.format("%8so%d.add(o%d);\n",
+ SPACE, element.parent.ref, element.ref));
+ }
+ }
+
+ // If an ID was specified, add the value to the named object map
+ if (id != null) {
+ if (id.length() == 0) {
+ throw new IllegalArgumentException(WTKXSerializer.WTKX_PREFIX
+ + ":" + WTKXSerializer.ID_ATTRIBUTE
+ + " must not be null.");
+ }
+
+ writer.write(String.format
+ ("%8snamedObjects.put(\"%s\", o%d);\n", SPACE, id, element.ref));
+ }
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case XMLStreamConstants.END_ELEMENT: {
+ String localName = reader.getLocalName();
+
+ switch (element.type) {
+ case WRITABLE_PROPERTY: {
+ Class<?> type = (Class<?>)element.clazz;
+ Class<?> parentType = (Class<?>)element.parent.clazz;
+ Method setterMethod = BeanDictionary.getSetterMethod(parentType, localName, type);
+
+ writer.write(String.format("%8so%d.%s(o%d);\n",
+ SPACE, element.parent.ref, setterMethod.getName(), element.ref));
+
+ break;
+ }
+
+ default: {
+ Class<?> type = (Class<?>)element.clazz;
+
+ if (type != null && Dictionary.class.isAssignableFrom(type)) {
+ // The element is an untyped object
+ for (Attribute attribute : element.attributes) {
+ if (Character.isUpperCase(attribute.localName.charAt(0))) {
+ throw new SerializationException
+ ("Static setters are only supported for typed instances.");
+ }
+
+ // Resolve and apply the attribute
+ writer.write(String.format("%8so%d.put(\"%s\", %s);\n", SPACE, element.ref,
+ attribute.localName, resolve(attribute.value, null)));
+ }
+ } else {
+ // The element represents a typed object; apply the attributes
+ for (Attribute attribute : element.attributes) {
+ Class<?> attributeType = BeanDictionary.getType(type, attribute.localName);
+
+ if (Character.isUpperCase(attribute.localName.charAt(0))) {
+ // The property represents an attached value
+ String propertyName = attribute.localName.substring
+ (attribute.localName.lastIndexOf(".") + 1);
+ propertyName = Character.toUpperCase(propertyName.charAt(0)) +
+ propertyName.substring(1);
+ String setterMethodName = BeanDictionary.SET_PREFIX + propertyName;
+
+ String propertyClassName = attribute.namespaceURI + "."
+ + attribute.localName.substring(0, attribute.localName.length()
+ - (propertyName.length() + 1));
+
+ writer.write(String.format("%8s%s.%s(o%d, %s);\n", SPACE, propertyClassName,
+ setterMethodName, element.ref, resolve(attribute.value, attributeType)));
+ } else {
+ if (attributeType != null
+ && ListenerList.class.isAssignableFrom(attributeType)) {
+ // The property represents a listener list
+ /*
+ ListenerList<Object> listenerList = (ListenerList<Object>)
+ valueDictionary.get(attribute.localName);
+
+ // The attribute value is a comma-separated list of listener IDs
+ String[] listenerIDs = attribute.value.split(",");
+
+ for (int i = 0, n = listenerIDs.length; i < n; i++) {
+ String listenerID = listenerIDs[i].trim();
+
+ if (listenerID.length() > 0) {
+ listenerID = listenerID.substring(1);
+
+ if (listenerID.length() > 0) {
+ listenerList.add(namedObjectDictionary.get(listenerID));
+ }
+ }
+ }
+ */
+ } else {
+ String key = Character.toUpperCase(attribute.localName.charAt(0)) +
+ attribute.localName.substring(1);
+ String setterMethodName = BeanDictionary.SET_PREFIX + key;
+
+ writer.write(String.format("%8so%d.%s(%s);\n", SPACE, element.ref,
+ setterMethodName, resolve(attribute.value, attributeType)));
+ }
+ }
+ }
+ }
+
+ // If the parent element is a writable property, set this as its value; it
+ // will be applied later in the parent's closing tag
+ if (element.parent != null
+ && element.parent.type == Element.Type.WRITABLE_PROPERTY) {
+ element.parent.clazz = element.clazz;
+ element.parent.ref = element.ref;
+ }
+ }
+ }
+
+ // If this is the top of the stack, return this element's value;
+ // otherwise, move up the stack
+ if (element.parent == null) {
+ writer.write(String.format("%8sresult = o%d;\n", SPACE, element.ref));
+ } else {
+ element = element.parent;
+ }
+
+ break;
+ }
+ }
+ }
+
+ reader.close();
+ } catch(XMLStreamException exception) {
+ throw new SerializationException(exception);
+ }
+
+ // Close method declaration
+ writer.write(String.format("%8sreturn (T)result;\n", SPACE));
+ writer.write(String.format("%4s}\n", SPACE));
+
+ // Close class declaration
+ writer.write("}\n");
+ } finally {
+ writer.close();
+ }
+
+ return javaFile;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
+ // TODO
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public java.util.Set<SourceVersion> getSourceVersions() {
+ return java.util.Collections.unmodifiableSet
+ (java.util.EnumSet.range(SourceVersion.RELEASE_5, SourceVersion.latest()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int isSupportedOption(String option) {
+ // TODO
+ return -1;
+ }
+
+ private String resolve(String attributeValue, Class<?> propertyType)
+ throws IOException {
+ String result = null;
+
+ if (propertyType == Boolean.class
+ || propertyType == Boolean.TYPE) {
+ result = String.valueOf(Boolean.parseBoolean(attributeValue));
+ } else if (propertyType == Character.class
+ || propertyType == Character.TYPE) {
+ if (attributeValue.length() > 0) {
+ result = String.format("'%c'", attributeValue.charAt(0));
+ }
+ } else if (propertyType == Byte.class
+ || propertyType == Byte.TYPE) {
+ try {
+ result = String.format("(byte)%d", Byte.parseByte(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else if (propertyType == Short.class
+ || propertyType == Short.TYPE) {
+ try {
+ result = String.format("(short)%d", Short.parseShort(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else if (propertyType == Integer.class
+ || propertyType == Integer.TYPE) {
+ try {
+ result = String.format("(int)%d", Integer.parseInt(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else if (propertyType == Long.class
+ || propertyType == Long.TYPE) {
+ try {
+ result = String.format("(long)%d", Long.parseLong(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else if (propertyType == Float.class
+ || propertyType == Float.TYPE) {
+ try {
+ result = String.format("(float)%f", Float.parseFloat(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else if (propertyType == Double.class
+ || propertyType == Double.TYPE) {
+ try {
+ result = String.format("(double)%f", Double.parseDouble(attributeValue));
+ } catch(NumberFormatException exception) {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else {
+ if (attributeValue.length() > 0) {
+ if (attributeValue.charAt(0) == WTKXSerializer.URL_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == WTKXSerializer.URL_PREFIX) {
+ result = String.format("\"%s\"", attributeValue.substring(1));
+ } else {
+ result = String.format("getClass().getResource(\"%s\")", attributeValue.substring(1));
+ }
+ }
+ } else if (attributeValue.charAt(0) == WTKXSerializer.RESOURCE_KEY_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == WTKXSerializer.RESOURCE_KEY_PREFIX) {
+ result = String.format("\"%s\"", attributeValue.substring(1));
+ } else {
+ // TODO
+ result = String.format("\"%s\"", attributeValue);
+ }
+ }
+ } else if (attributeValue.charAt(0) == WTKXSerializer.OBJECT_REFERENCE_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == WTKXSerializer.OBJECT_REFERENCE_PREFIX) {
+ result = String.format("\"%s\"", attributeValue.substring(1));
+ } else {
+ String className = propertyType == null ?
+ "Object" : propertyType.getName();
+ result = String.format("(%s)namedObjects.get(\"%s\")",
+ className, attributeValue.substring(1));
+ }
+ }
+ } else {
+ result = String.format("\"%s\"", attributeValue);
+ }
+ } else {
+ result = "\"\"";
+ }
+ }
+
+ if (result == null) {
+ // Fall-through case
+ result = "null";
+ }
+
+ return result;
+ }
+}
Modified: incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java?rev=775969&r1=775968&r2=775969&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java Mon May 18 14:43:24 2009
@@ -52,7 +52,7 @@
*
* @author gbrown
*/
-public class WTKXSerializer implements Serializer<Object> {
+public class WTKXSerializer implements Serializer<Object>, Bindable.ObjectHierarchy {
private static class Element {
public enum Type {
INSTANCE,
@@ -213,6 +213,7 @@
private URL location = null;
private Resources resources = null;
+ private Object rootObject = null;
private HashMap<String, Object> namedObjects = new HashMap<String, Object>();
private HashMap<String, WTKXSerializer> includeSerializers = new HashMap<String, WTKXSerializer>();
@@ -703,6 +704,9 @@
// subsequent call to this method
location = null;
+ // Set the root object
+ rootObject = object;
+
return object;
}
@@ -745,6 +749,21 @@
}
/**
+ * Retrieves the root object of the WTKX hierarchy.
+ *
+ * @param <T>
+ * The type of the object to return.
+ *
+ * @return
+ * The root object, or <tt>null</tt> if this serializer has not yet read an
+ * object from an input stream.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getRootObject() {
+ return (T)rootObject;
+ }
+
+ /**
* Retrieves a named object.
*
* @param <T>