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/03/26 00:12:17 UTC
svn commit: r758461 [47/47] - in /incubator/pivot/branches: ./ 1.1/
1.1/charts-test/ 1.1/charts-test/src/ 1.1/charts-test/src/pivot/
1.1/charts-test/src/pivot/charts/ 1.1/charts-test/src/pivot/charts/test/
1.1/charts/ 1.1/charts/lib/ 1.1/charts/src/ 1....
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/WTKXSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/WTKXSerializer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/WTKXSerializer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/WTKXSerializer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,903 @@
+/*
+ * Copyright (c) 2009 VMware, Inc.
+ *
+ * Licensed 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.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+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.beans.PropertyNotFoundException;
+import pivot.collections.ArrayList;
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.collections.List;
+import pivot.collections.Sequence;
+import pivot.serialization.Serializer;
+import pivot.serialization.SerializationException;
+import pivot.util.ListenerList;
+import pivot.util.Resources;
+
+/**
+ * Loads an object hierarchy from an XML document.
+ *
+ * @author gbrown
+ */
+public class WTKXSerializer implements Serializer<Object> {
+ private static class Element {
+ public enum Type {
+ INSTANCE,
+ INCLUDE,
+ SCRIPT,
+ READ_ONLY_PROPERTY,
+ WRITABLE_PROPERTY
+ }
+
+ public final Element parent;
+ public final String tagName;
+ public final Type type;
+ public final List<Attribute> attributes;
+
+ public Object value;
+
+ public Element(Element parent, String tagName, Type type, List<Attribute> attributes, Object value) {
+ this.parent = parent;
+ this.tagName = tagName;
+ this.type = type;
+ this.attributes = attributes;
+ this.value = value;
+ }
+ }
+
+ 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 URL location = null;
+ private Resources resources = null;
+
+ private HashMap<String, Object> namedObjects = new HashMap<String, Object>();
+ private HashMap<String, WTKXSerializer> includeSerializers = new HashMap<String, WTKXSerializer>();
+
+ private XMLInputFactory xmlInputFactory;
+ private Object scriptEngineManager;
+ private Class<?> scriptEngineManagerClass;
+
+ public static final char URL_PREFIX = '@';
+ public static final char RESOURCE_KEY_PREFIX = '%';
+ public static final char OBJECT_REFERENCE_PREFIX = '$';
+
+ public static final String WTKX_PREFIX = "wtkx";
+ public static final String ID_ATTRIBUTE = "id";
+
+ public static final String INCLUDE_TAG = "include";
+ public static final String INCLUDE_SRC_ATTRIBUTE = "src";
+ public static final String INCLUDE_NAMESPACE_ATTRIBUTE = "namespace";
+ public static final String INCLUDE_RESOURCES_ATTRIBUTE = "resources";
+
+ public static final String SCRIPT_TAG = "script";
+ public static final String SCRIPT_SRC_ATTRIBUTE = "src";
+
+ public static final String MIME_TYPE = "application/wtkx";
+
+ public WTKXSerializer() {
+ this(null);
+ }
+
+ public WTKXSerializer(Resources resources) {
+ this.resources = resources;
+
+ xmlInputFactory = XMLInputFactory.newInstance();
+ xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true);
+
+ try {
+ scriptEngineManagerClass = Class.forName("javax.script.ScriptEngineManager");
+ scriptEngineManager = scriptEngineManagerClass.newInstance();
+ } catch(Exception exception) {
+ scriptEngineManagerClass = null;
+ scriptEngineManager = null;
+ }
+ }
+
+ public Resources getResources() {
+ return resources;
+ }
+
+ public Object readObject(String resourceName) throws IOException,
+ SerializationException {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ URL location = classLoader.getResource(resourceName);
+
+ if (location == null) {
+ throw new SerializationException("Could not find resource named \""
+ + resourceName + "\".");
+ }
+
+ return readObject(location);
+ }
+
+ public Object readObject(URL location) throws IOException, SerializationException {
+ this.location = location;
+ return readObject(new BufferedInputStream(location.openStream()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object readObject(InputStream inputStream) throws IOException,
+ SerializationException {
+ Object object = null;
+
+ // Clear any previous named objects and include serializers
+ namedObjects.clear();
+ includeSerializers.clear();
+
+ // 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: {
+ if (!reader.isWhiteSpace()) {
+ String text = reader.getText();
+
+ if (text.length() > 0) {
+ switch (element.type) {
+ case INSTANCE: {
+ if (element.value instanceof Sequence) {
+ Sequence<Object> sequence = (Sequence<Object>)element.value;
+
+ try {
+ Method addMethod = sequence.getClass().getMethod("add",
+ new Class<?>[] {String.class});
+ addMethod.invoke(sequence, new Object[] {text});
+ } catch(NoSuchMethodException exception) {
+ throw new SerializationException("Text content cannot be added to "
+ + sequence.getClass().getName() + ".", exception);
+ } catch(InvocationTargetException exception) {
+ throw new SerializationException(exception);
+ } catch(IllegalAccessException exception) {
+ throw new SerializationException(exception);
+ }
+ }
+
+ break;
+ }
+
+ case WRITABLE_PROPERTY: {
+ element.value = text;
+ break;
+ }
+ }
+ }
+ }
+
+ 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(WTKX_PREFIX)) {
+ if (element == null) {
+ throw new SerializationException(prefix + ":" + localName
+ + " is not a valid root element.");
+ }
+
+ if (localName.equals(INCLUDE_TAG)) {
+ // The element represents an include
+ String src = null;
+ String namespace = null;
+
+ Resources includeResources = resources;
+
+ 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(WTKX_PREFIX)) {
+ if (attributeLocalName.equals(ID_ATTRIBUTE)) {
+ id = attributeValue;
+ }
+ } else {
+ if (attributeLocalName.equals(INCLUDE_SRC_ATTRIBUTE)) {
+ src = attributeValue;
+ } else if (attributeLocalName.equals(INCLUDE_NAMESPACE_ATTRIBUTE)) {
+ namespace = attributeValue;
+ } else if (attributeLocalName.equals(INCLUDE_RESOURCES_ATTRIBUTE)) {
+ includeResources = new Resources(attributeValue);
+ } else {
+ attributes.add(new Attribute(attributeNamespaceURI,
+ attributePrefix, attributeLocalName, attributeValue));
+ }
+ }
+ }
+
+ if (src == null) {
+ throw new SerializationException(INCLUDE_SRC_ATTRIBUTE
+ + " attribute is required for " + WTKX_PREFIX + ":" + INCLUDE_TAG
+ + " tag.");
+ }
+
+ // Process the include
+ WTKXSerializer serializer = new WTKXSerializer(includeResources);
+ if (namespace != null) {
+ includeSerializers.put(namespace, serializer);
+ }
+
+ Object value;
+ if (src.charAt(0) == '/') {
+ value = serializer.readObject(src.substring(1));
+ } else {
+ value = serializer.readObject(new URL(location, src));
+ }
+
+ element = new Element(element, localName, Element.Type.INCLUDE, attributes, value);
+ } else if (localName.equals(SCRIPT_TAG)) {
+ if (scriptEngineManagerClass == null) {
+ throw new SerializationException("Scripting is not supported on this platform.");
+ }
+
+ // The element represents a script
+ String src = null;
+
+ for (int i = 0, n = reader.getAttributeCount(); i < n; i++) {
+ String attributeLocalName = reader.getAttributeLocalName(i);
+ String attributeValue = reader.getAttributeValue(i);
+
+ if (attributeLocalName.equals(SCRIPT_SRC_ATTRIBUTE)) {
+ src = attributeValue;
+ } else {
+ throw new SerializationException(attributeLocalName + " is not a valid "
+ + " attribute for the " + WTKX_PREFIX + ":" + SCRIPT_TAG + ".");
+ }
+ }
+
+ if (src == null) {
+ throw new SerializationException(INCLUDE_SRC_ATTRIBUTE
+ + " attribute is required for " + WTKX_PREFIX + ":" + SCRIPT_TAG
+ + " tag.");
+ }
+
+ int i = src.lastIndexOf(".");
+ if (i == -1) {
+ throw new SerializationException("Cannot determine type of script \""
+ + src + "\".");
+ }
+
+ String extension = src.substring(i + 1);
+
+ Object scriptEngine = null;
+ try {
+ Method getEngineByExtensionMethod =
+ scriptEngineManagerClass.getMethod("getEngineByExtension", new Class<?>[] {String.class});
+
+ scriptEngine = getEngineByExtensionMethod.invoke(scriptEngineManager, new Object[] {extension});
+ } catch(Exception exception) {
+ throw new RuntimeException(exception);
+ }
+
+ if (scriptEngine == null) {
+ throw new SerializationException("Unable to find scripting engine for "
+ + " extension " + extension + ".");
+ }
+
+ try {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ URL scriptLocation;
+
+ if (src.charAt(0) == '/') {
+ scriptLocation = classLoader.getResource(src);
+ } else {
+ scriptLocation = new URL(location, src);
+ }
+
+ Class<?> bindingsClass = Class.forName("javax.script.Bindings");
+ Method evalMethod = scriptEngine.getClass().getMethod("eval",
+ new Class<?>[] {Reader.class, bindingsClass});
+
+ Method getBindingsMethod =
+ scriptEngineManagerClass.getMethod("getBindings", new Class<?>[] {});
+
+ Object bindings = getBindingsMethod.invoke(scriptEngineManager, new Object[] {});
+ Reader scriptReader = new BufferedReader(new InputStreamReader(scriptLocation.openStream()));
+ evalMethod.invoke(scriptEngine, new Object[] {scriptReader, bindings});
+ } catch(Exception exception) {
+ throw new SerializationException(exception);
+ }
+
+ element = new Element(element, localName, Element.Type.SCRIPT, null, null);
+ } 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(WTKX_PREFIX)) {
+ if (attributeLocalName.equals(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.replace('.', '$');
+
+ try {
+ Class<?> type = Class.forName(className);
+ element = new Element(element, localName, Element.Type.INSTANCE, attributes, type.newInstance());
+ } catch(Exception exception) {
+ throw new SerializationException(exception);
+ }
+ } else {
+ // This element represents a property
+ if (element == null) {
+ throw new SerializationException("Root node must represent a typed object.");
+ }
+
+ BeanDictionary propertyDictionary = new BeanDictionary(element.value);
+
+ if (propertyDictionary.isReadOnly(localName)) {
+ Object value = propertyDictionary.get(localName);
+ assert (value != null) : "Read-only properties cannot be null.";
+ element = new Element(element, localName, Element.Type.READ_ONLY_PROPERTY, attributes, value);
+ } else {
+ if (attributes.getLength() > 0) {
+ throw new SerializationException("Writable property elements cannot have attributes.");
+ }
+
+ element = new Element(element, localName, Element.Type.WRITABLE_PROPERTY, null, null);
+ }
+ }
+ }
+
+ 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) {
+ if (element.parent.value instanceof Sequence) {
+ Sequence<Object> sequence = (Sequence<Object>)element.parent.value;
+ sequence.add(element.value);
+ } else {
+ if (element.parent.value instanceof ListenerList) {
+ ListenerList<Object> listenerList = (ListenerList<Object>)element.parent.value;
+ listenerList.add(element.value);
+ }
+ }
+ }
+
+ // If an ID was specified, add the value to the named object map
+ if (id != null) {
+ namedObjects.put(id, element.value);
+
+ if (scriptEngineManager != null) {
+ try {
+ Method putMethod = scriptEngineManagerClass.getMethod("put",
+ new Class<?>[] {String.class, Object.class});
+ putMethod.invoke(scriptEngineManager, new Object[] {id, namedObjects.get(id)});
+ } catch(Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case XMLStreamConstants.END_ELEMENT: {
+ String localName = reader.getLocalName();
+
+ switch (element.type) {
+ case WRITABLE_PROPERTY: {
+ BeanDictionary propertyDictionary = new BeanDictionary(element.parent.value);
+ propertyDictionary.put(localName, element.value);
+ break;
+ }
+
+ case SCRIPT: {
+ break;
+ }
+
+ default: {
+ if (element.value instanceof Dictionary) {
+ // The element is an untyped object
+ Dictionary<String, Object> dictionary = (Dictionary<String, Object>)element.value;
+
+ 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
+ dictionary.put(attribute.localName, resolve(attribute.value, null));
+ }
+ } else {
+ // The element represents a typed object; apply the attributes
+ BeanDictionary valueDictionary = new BeanDictionary(element.value);
+
+ for (Attribute attribute : element.attributes) {
+ if (Character.isUpperCase(attribute.localName.charAt(0))) {
+ // The property represents an attached value
+ setStaticProperty(attribute, element.value);
+ } else {
+ Class<?> type = valueDictionary.getType(attribute.localName);
+
+ if (type != null
+ && ListenerList.class.isAssignableFrom(type)) {
+ // 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(getObjectByID(listenerID));
+ }
+ }
+ }
+ } else {
+ valueDictionary.put(attribute.localName,
+ resolve(attribute.value, valueDictionary.getType(attribute.localName)));
+ }
+ }
+ }
+ }
+
+ // 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.value = element.value;
+ }
+ }
+ }
+
+ // If this is the top of the stack, return this element's value;
+ // otherwise, move up the stack
+ if (element.parent == null) {
+ object = element.value;
+ } else {
+ element = element.parent;
+ }
+
+ break;
+ }
+ }
+ }
+
+ reader.close();
+ } catch(XMLStreamException exception) {
+ throw new SerializationException(exception);
+ }
+
+ // Clear the location so the previous value won't be re-used in a
+ // subsequent call to this method
+ location = null;
+
+ return object;
+ }
+
+ public void writeObject(Object object, OutputStream outputStream) throws IOException,
+ SerializationException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getMIMEType(Object object) {
+ return MIME_TYPE;
+ }
+
+ /**
+ * Retrieves a included serializer by its namespace.
+ *
+ * @param name
+ * The name of the serializer, relative to this loader. The values's name
+ * is the concatentation of its parent namespaces and its namespace,
+ * separated by periods (e.g. "foo.bar.baz").
+ *
+ * @return The named serializer, or <tt>null</tt> if a serializer with the
+ * given name does not exist.
+ */
+ public WTKXSerializer getSerializerByName(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name is null.");
+ }
+
+ WTKXSerializer serializer = this;
+ String[] namespacePath = name.split("\\.");
+
+ int i = 0;
+ int n = namespacePath.length;
+ while (i < n && serializer != null) {
+ String namespace = namespacePath[i++];
+ serializer = serializer.includeSerializers.get(namespace);
+ }
+
+ return serializer;
+ }
+
+ /**
+ * Retrieves a named object.
+ *
+ * @param name
+ * The name of the object, relative to this loader. The values's name is the
+ * concatentation of its parent namespaces and its ID, separated by periods
+ * (e.g. "foo.bar.baz").
+ *
+ * @return The named object, or <tt>null</tt> if an object with the given
+ * name does not exist.
+ */
+ public Object getObjectByName(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name is null.");
+ }
+
+ Object object = null;
+ WTKXSerializer serializer = this;
+ String[] namespacePath = name.split("\\.");
+
+ int i = 0;
+ int n = namespacePath.length - 1;
+ while (i < n && serializer != null) {
+ String namespace = namespacePath[i++];
+ serializer = serializer.includeSerializers.get(namespace);
+ }
+
+ if (serializer != null) {
+ object = serializer.getObjectByID(namespacePath[i]);
+ }
+
+ return object;
+ }
+
+ private Object getObjectByID(String id) {
+ Object object = null;
+
+ if (namedObjects.containsKey(id)) {
+ object = namedObjects.get(id);
+ } else {
+ if (scriptEngineManager != null) {
+ try {
+ Method getMethod = scriptEngineManagerClass.getMethod("get", new Class<?>[] {String.class});
+ object = getMethod.invoke(scriptEngineManager, new Object[] {id});
+ } catch(Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+ }
+
+ return object;
+ }
+
+ /**
+ * Resolves an attribute value. If the property type is a primitive or
+ * primitive wrapper, converts the string value to the primitive type.
+ * Otherwise, resolves the value as either a URL, resource value, or
+ * object reference, depending on the value's prefix. If the value can't
+ * or doesn't need to be resolved, the original attribute value is
+ * returned.
+ *
+ * @param attributeValue
+ * The attribute value to resolve.
+ *
+ * @param propertyType
+ * The property type, or <tt>null</tt> if the type is not known.
+ *
+ * @return
+ * The resolved value.
+ */
+ private Object resolve(String attributeValue, Class<?> propertyType)
+ throws MalformedURLException {
+ Object resolvedValue = null;
+
+ if (propertyType == Boolean.class
+ || propertyType == Boolean.TYPE) {
+ try {
+ resolvedValue = Boolean.parseBoolean(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Character.class
+ || propertyType == Character.TYPE) {
+ if (attributeValue.length() > 0) {
+ resolvedValue = attributeValue.charAt(0);
+ }
+ } else if (propertyType == Byte.class
+ || propertyType == Byte.TYPE) {
+ try {
+ resolvedValue = Byte.parseByte(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Short.class
+ || propertyType == Short.TYPE) {
+ try {
+ resolvedValue = Short.parseShort(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Integer.class
+ || propertyType == Integer.TYPE) {
+ try {
+ resolvedValue = Integer.parseInt(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Long.class
+ || propertyType == Long.TYPE) {
+ try {
+ resolvedValue = Long.parseLong(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Float.class
+ || propertyType == Float.TYPE) {
+ try {
+ resolvedValue = Float.parseFloat(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else if (propertyType == Double.class
+ || propertyType == Double.TYPE) {
+ try {
+ resolvedValue = Double.parseDouble(attributeValue);
+ } catch(NumberFormatException exception) {
+ resolvedValue = attributeValue;
+ }
+ } else {
+ if (attributeValue.charAt(0) == URL_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == URL_PREFIX) {
+ resolvedValue = attributeValue.substring(1);
+ } else {
+ if (location == null) {
+ throw new IllegalStateException("Base location is undefined.");
+ }
+
+ resolvedValue = new URL(location, attributeValue.substring(1));
+ }
+ }
+ } else if (attributeValue.charAt(0) == RESOURCE_KEY_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == RESOURCE_KEY_PREFIX) {
+ resolvedValue = attributeValue.substring(1);
+ } else {
+ if (resources == null) {
+ throw new IllegalStateException("Resource dictionary is undefined.");
+ }
+
+ resolvedValue = resources.get(attributeValue.substring(1));
+ }
+ }
+ } else if (attributeValue.charAt(0) == OBJECT_REFERENCE_PREFIX) {
+ if (attributeValue.length() > 1) {
+ if (attributeValue.charAt(1) == OBJECT_REFERENCE_PREFIX) {
+ resolvedValue = attributeValue.substring(1);
+ } else {
+ resolvedValue = getObjectByID(attributeValue.substring(1));
+ }
+ }
+ } else {
+ resolvedValue = attributeValue;
+ }
+ }
+
+ return resolvedValue;
+ }
+
+ /**
+ * Invokes a static property setter.
+ *
+ * @param attribute
+ * The attribute whose corresponding static setter is to be invoked.
+ *
+ * @param object
+ * The object on which to invoke the static setter.
+ */
+ private void setStaticProperty(Attribute attribute, Object object)
+ throws SerializationException, MalformedURLException {
+ String propertyName =
+ attribute.localName.substring(attribute.localName.lastIndexOf(".") + 1);
+ propertyName = Character.toUpperCase(propertyName.charAt(0)) +
+ propertyName.substring(1);
+
+ String propertyClassName = attribute.namespaceURI + "."
+ + attribute.localName.substring(0, attribute.localName.length()
+ - (propertyName.length() + 1));
+
+ Class<?> propertyClass = null;
+ try {
+ propertyClass = Class.forName(propertyClassName);
+ } catch(ClassNotFoundException exception) {
+ throw new SerializationException(exception);
+ }
+
+ Class<?> objectType = object.getClass();
+
+ // Determine the property type from the getter method
+ Method getterMethod = getStaticGetterMethod(propertyClass, propertyName, objectType);
+ if (getterMethod == null) {
+ throw new PropertyNotFoundException("Static property \"" + attribute
+ + "\" does not exist.");
+ }
+
+ // Resolve the attribute value
+ Class<?> propertyType = getterMethod.getReturnType();
+ Object propertyValue = resolve(attribute.value, propertyType);
+ Class<?> propertyValueType = (propertyValue == null) ?
+ getterMethod.getReturnType() : propertyValue.getClass();
+
+ Method setterMethod = getStaticSetterMethod(propertyClass, propertyName,
+ objectType, propertyValueType);
+
+ if (setterMethod == null) {
+ throw new SerializationException("Unable to determine type for "
+ + " static property \"" + attribute + "\".");
+ }
+
+ // Invoke the setter
+ try {
+ setterMethod.invoke(null, new Object[] {object, propertyValue});
+ } catch(Exception exception) {
+ throw new SerializationException(exception);
+ }
+ }
+
+ private Method getStaticGetterMethod(Class<?> propertyClass, String propertyName,
+ Class<?> objectType) {
+ Method method = null;
+
+ if (objectType != null) {
+ try {
+ method = propertyClass.getMethod(BeanDictionary.GET_PREFIX
+ + propertyName, new Class<?>[] {objectType});
+ } catch(NoSuchMethodException exception) {
+ // No-op
+ }
+
+ if (method == null) {
+ try {
+ method = propertyClass.getMethod(BeanDictionary.IS_PREFIX
+ + propertyName, new Class<?>[] {objectType});
+ } catch(NoSuchMethodException exception) {
+ // No-op
+ }
+ }
+
+ if (method == null) {
+ method = getStaticGetterMethod(propertyClass, propertyName,
+ objectType.getSuperclass());
+ }
+ }
+
+ return method;
+ }
+
+ private Method getStaticSetterMethod(Class<?> propertyClass, String propertyName,
+ Class<?> objectType, Class<?> propertyValueType) {
+ Method method = null;
+
+ if (objectType != null) {
+ final String methodName = BeanDictionary.SET_PREFIX + propertyName;
+
+ try {
+ method = propertyClass.getMethod(methodName,
+ new Class<?>[] {objectType, propertyValueType});
+ } catch(NoSuchMethodException exception) {
+ // No-op
+ }
+
+ if (method == null) {
+ // If value type is a primitive wrapper, look for a method
+ // signature with the corresponding primitive type
+ try {
+ Field primitiveTypeField = propertyValueType.getField("TYPE");
+ Class<?> primitivePropertyValueType = (Class<?>)primitiveTypeField.get(null);
+
+ try {
+ method = propertyClass.getMethod(methodName,
+ new Class<?>[] {objectType, primitivePropertyValueType});
+ } catch(NoSuchMethodException exception) {
+ // No-op
+ }
+ } catch(NoSuchFieldException exception) {
+ // No-op; not a wrapper type
+ } catch(IllegalAccessException exception) {
+ // No-op; not a wrapper type
+ }
+ }
+
+ if (method == null) {
+ method = getStaticSetterMethod(propertyClass, propertyName,
+ objectType.getSuperclass(), propertyValueType);
+ }
+ }
+
+ return method;
+ }
+}
Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/package.html?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/package.html (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtkx/package.html Wed Mar 25 23:08:38 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Provides support for loading WTK components and other classes from XML.</p>
+</body>
+</html>