You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2010/07/09 15:35:43 UTC

svn commit: r962522 - /pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java

Author: gbrown
Date: Fri Jul  9 13:35:43 2010
New Revision: 962522

URL: http://svn.apache.org/viewvc?rev=962522&view=rev
Log:
Internal BXMLSerializer cleanup.

Modified:
    pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java

Modified: pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java?rev=962522&r1=962521&r2=962522&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java Fri Jul  9 13:35:43 2010
@@ -46,10 +46,9 @@ import javax.xml.stream.XMLStreamConstan
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
 
-import org.apache.pivot.collections.ArrayList;
 import org.apache.pivot.collections.Dictionary;
 import org.apache.pivot.collections.HashMap;
-import org.apache.pivot.collections.List;
+import org.apache.pivot.collections.LinkedList;
 import org.apache.pivot.collections.Map;
 import org.apache.pivot.collections.Sequence;
 import org.apache.pivot.json.JSON;
@@ -168,31 +167,28 @@ public class BXMLSerializer implements S
         public final Element parent;
         public final String namespaceURI;
         public final String localName;
-        public final List<Attribute> attributes;
         public final Type type;
-        public final String id;
         public Object value;
-        public final int lineNumber;
 
-        public Element(Element parent, String namespaceURI, String localName, List<Attribute> attributes,
-            Type type, String id, Object value, int lineNumber) {
+        public final LinkedList<Attribute> attributes = new LinkedList<Attribute>();
+
+        public Element(Element parent, String namespaceURI, String localName, Type type, Object value) {
             this.parent = parent;
             this.namespaceURI = namespaceURI;
             this.localName = localName;
-            this.attributes = attributes;
             this.type = type;
-            this.id = id;
             this.value = value;
-            this.lineNumber = lineNumber;
         }
     }
 
     private static class Attribute {
+        public final Element element;
         public final String namespaceURI;
         public final String localName;
-        public final String value;
+        public final Object value;
 
-        public Attribute(String namespaceURI, String localName, String value) {
+        public Attribute(Element element, String namespaceURI, String localName, Object value) {
+            this.element = element;
             this.namespaceURI = namespaceURI;
             this.localName = localName;
             this.value = value;
@@ -292,10 +288,13 @@ public class BXMLSerializer implements S
     private URL location = null;
     private Resources resources = null;
 
+    private String language = DEFAULT_LANGUAGE;
+
+    private XMLStreamReader xmlStreamReader = null;
     private Element element = null;
-    private Object root = null;
+    private LinkedList<Attribute> objectReferenceAttributes = new LinkedList<Attribute>();
 
-    private String language = DEFAULT_LANGUAGE;
+    private Object root = null;
 
     private static HashMap<String, String> fileExtensions = new HashMap<String, String>();
     private static HashMap<String, Class<? extends Serializer<?>>> mimeTypes =
@@ -371,11 +370,9 @@ public class BXMLSerializer implements S
         language = DEFAULT_LANGUAGE;
 
         // Parse the XML stream
-        element = null;
-
         try {
             try {
-                XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
+                xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
 
                 while (xmlStreamReader.hasNext()) {
                     int event = xmlStreamReader.next();
@@ -416,6 +413,10 @@ public class BXMLSerializer implements S
             throw exception;
         }
 
+        xmlStreamReader = null;
+
+        objectReferenceAttributes.clear();
+
         if (root instanceof Bindable) {
             bind(root);
             Bindable bindable = (Bindable)root;
@@ -529,45 +530,16 @@ public class BXMLSerializer implements S
         // Get element properties
         String namespaceURI = xmlStreamReader.getNamespaceURI();
         String prefix = xmlStreamReader.getPrefix();
-        String localName = xmlStreamReader.getLocalName();
 
-        // Build attribute list; these will be processed in the close tag
-        String id = null;
-        ArrayList<Attribute> attributes = new ArrayList<Attribute>();
-
-        for (int i = 0, n = xmlStreamReader.getAttributeCount(); i < n; i++) {
-            String attributePrefix = xmlStreamReader.getAttributePrefix(i);
-            String attributeLocalName = xmlStreamReader.getAttributeLocalName(i);
-            String attributeValue = xmlStreamReader.getAttributeValue(i);
-
-            if (attributePrefix != null
-                && attributePrefix.equals(internalNamespacePrefix)) {
-                if (attributeLocalName.equals(ID_ATTRIBUTE)) {
-                    if (attributeValue.length() == 0) {
-                        throw new IllegalArgumentException(internalNamespacePrefix + ":" + ID_ATTRIBUTE
-                            + " must not be empty.");
-                    }
-
-                    if (attributeValue.contains(".")) {
-                        throw new IllegalArgumentException("\"" + attributeValue + "\" is not a valid ID value.");
-                    }
-
-                    id = attributeValue;
-                } else {
-                    throw new SerializationException(internalNamespacePrefix + ":" + attributeLocalName
-                        + " is not a valid attribute.");
-                }
-            } else {
-                String attributeNamespaceURI = xmlStreamReader.getAttributeNamespace(i);
-                if (attributeNamespaceURI == null) {
-                    attributeNamespaceURI = xmlStreamReader.getNamespaceURI("");
-                }
-
-                attributes.add(new Attribute(attributeNamespaceURI, attributeLocalName,
-                    attributeValue));
-            }
+        // Some stream readers incorrectly report an empty string as the prefix
+        // for the default namespace
+        if (prefix != null
+            && prefix.length() == 0) {
+            prefix = null;
         }
 
+        String localName = xmlStreamReader.getLocalName();
+
         // Determine the type and value of this element
         Element.Type elementType = null;
         Object value = null;
@@ -585,11 +557,6 @@ public class BXMLSerializer implements S
             } else if (localName.equals(SCRIPT_TAG)) {
                 elementType = Element.Type.SCRIPT;
             } else if (localName.equals(DEFINE_TAG)) {
-                if (attributes.getLength() > 0) {
-                    throw new SerializationException(internalNamespacePrefix + ":" + DEFINE_TAG
-                        + " cannot have attributes.");
-                }
-
                 elementType = Element.Type.DEFINE;
             } else {
                 throw new SerializationException(prefix + ":" + localName
@@ -598,10 +565,6 @@ public class BXMLSerializer implements S
         } else {
             if (Character.isUpperCase(localName.charAt(0))) {
                 int i = localName.indexOf('.');
-                if (i == localName.length() - 1) {
-                    throw new SerializationException(localName + " is not a valid element name.");
-                }
-
                 if (i != -1
                     && Character.isLowerCase(localName.charAt(i + 1))) {
                     // The element represents an attached property
@@ -629,22 +592,13 @@ public class BXMLSerializer implements S
                 }
             } else {
                 // The element represents a property
-                if (element == null) {
-                    throw new SerializationException("Cannot specify property as root element.");
-                }
-
-                if (prefix != null
-                    && prefix.length() > 0) {
+                if (prefix != null) {
                     throw new SerializationException("Property elements cannot have a namespace prefix.");
                 }
 
                 if (element.value instanceof Dictionary<?, ?>) {
                     elementType = Element.Type.WRITABLE_PROPERTY;
                 } else {
-                    if (element.type != Element.Type.INSTANCE) {
-                        throw new SerializationException("Parent element must be a typed object.");
-                    }
-
                     BeanAdapter beanAdapter = new BeanAdapter(element.value);
 
                     if (beanAdapter.isReadOnly(localName)) {
@@ -652,24 +606,122 @@ public class BXMLSerializer implements S
                         value = beanAdapter.get(localName);
                         assert (value != null) : "Read-only properties cannot be null.";
                     } else {
-                        if (attributes.getLength() > 0) {
-                            throw new SerializationException("Writable property elements cannot have attributes.");
-                        }
-
                         elementType = Element.Type.WRITABLE_PROPERTY;
                     }
                 }
             }
         }
 
-        // Set the current element
-        Location xmlStreamLocation = xmlStreamReader.getLocation();
-        element = new Element(element, namespaceURI, localName, attributes, elementType,
-            id, value, xmlStreamLocation.getLineNumber());
+        // Create the element
+        element = new Element(element, namespaceURI, localName, elementType, value);
+        processAttributes();
+    }
 
-        // If this is the root, set it
-        if (element.parent == null) {
-            root = element.value;
+    private void processAttributes() throws SerializationException {
+        String id = null;
+
+        for (int i = 0, n = xmlStreamReader.getAttributeCount(); i < n; i++) {
+            String attributePrefix = xmlStreamReader.getAttributePrefix(i);
+            String attributeLocalName = xmlStreamReader.getAttributeLocalName(i);
+            String attributeValue = xmlStreamReader.getAttributeValue(i);
+
+            if (attributePrefix != null
+                && attributePrefix.equals(internalNamespacePrefix)) {
+                if (attributeLocalName.equals(ID_ATTRIBUTE)) {
+                    if (attributeValue.length() == 0
+                        || attributeValue.contains(".")) {
+                        throw new IllegalArgumentException("\"" + attributeValue + "\" is not a valid ID value.");
+                    }
+
+                    id = attributeValue;
+                } else {
+                    throw new SerializationException(internalNamespacePrefix + ":" + attributeLocalName
+                        + " is not a valid attribute.");
+                }
+            } else {
+                String attributeNamespaceURI = xmlStreamReader.getAttributeNamespace(i);
+                if (attributeNamespaceURI == null) {
+                    attributeNamespaceURI = xmlStreamReader.getNamespaceURI("");
+                }
+
+                Object resolvedValue = null;
+
+                if (attributeValue.length() > 0) {
+                    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.");
+                                }
+
+                                try {
+                                    resolvedValue = new URL(location, attributeValue.substring(1));
+                                } catch (MalformedURLException exception) {
+                                    throw new SerializationException(exception);
+                                }
+                            }
+                        }
+                    } 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("Resources is null.");
+                                }
+
+                                resolvedValue = JSON.get(resources, attributeValue.substring(1));
+
+                                if (resolvedValue == null) {
+                                    resolvedValue = attributeValue;
+                                }
+                            }
+                        }
+                    } else if (attributeValue.charAt(0) == OBJECT_REFERENCE_PREFIX) {
+                        if (attributeValue.length() > 1) {
+                            if (attributeValue.charAt(1) == OBJECT_REFERENCE_PREFIX) {
+                                resolvedValue = attributeValue.substring(1);
+                            } else if (attributeValue.charAt(1) != NAMESPACE_BINDING_PREFIX) {
+                                // TODO The above check is temporary; this entire block needs to move
+                                // to processEndElement() or readObject()
+                                resolvedValue = JSON.get(namespace, attributeValue.substring(1));
+
+                                if (resolvedValue == null) {
+                                    resolvedValue = attributeValue;
+                                }
+                            }
+                        }
+                    } else {
+                        resolvedValue = attributeValue;
+                    }
+                } else {
+                    resolvedValue = attributeValue;
+                }
+
+                // TODO Don't do this for all attributes, only those that were resolved
+                element.attributes.add(new Attribute(element, attributeNamespaceURI, attributeLocalName,
+                    resolvedValue));
+            }
+        }
+
+        // Add the value to the namespace
+        if (id != null) {
+            if (namespace.containsKey(id)) {
+                throw new SerializationException("ID " + id + " is already in use.");
+            }
+
+            namespace.put(id, element.value);
+
+            // If the type has an ID property, use it
+            Class<?> type = element.value.getClass();
+            IDProperty idProperty = type.getAnnotation(IDProperty.class);
+
+            if (idProperty != null) {
+                BeanAdapter beanAdapter = new BeanAdapter(element.value);
+                beanAdapter.put(idProperty.value(), id);
+            }
         }
     }
 
@@ -681,8 +733,8 @@ public class BXMLSerializer implements S
         switch (element.type) {
             case INSTANCE:
             case INCLUDE: {
-                ArrayList<Attribute> instancePropertyAttributes = new ArrayList<Attribute>();
-                ArrayList<Attribute> staticPropertyAttributes = new ArrayList<Attribute>();
+                LinkedList<Attribute> instancePropertyAttributes = new LinkedList<Attribute>();
+                LinkedList<Attribute> staticPropertyAttributes = new LinkedList<Attribute>();
 
                 if (element.type == Element.Type.INCLUDE) {
                     // Process attributes looking for include parameters and property setters
@@ -691,15 +743,16 @@ public class BXMLSerializer implements S
                     String mimeType = null;
                     boolean inline = false;
 
+                    // TODO Move this to processStartElement(); we won't need to cast to String there
                     for (Attribute attribute : element.attributes) {
                         if (attribute.localName.equals(INCLUDE_SRC_ATTRIBUTE)) {
-                            src = attribute.value;
+                            src = (String)attribute.value;
                         } else if (attribute.localName.equals(INCLUDE_RESOURCES_ATTRIBUTE)) {
-                            resources = new Resources(resources, attribute.value);
+                            resources = new Resources(resources, (String)attribute.value);
                         } else if (attribute.localName.equals(INCLUDE_MIME_TYPE_ATTRIBUTE)) {
-                            mimeType = attribute.value;
+                            mimeType = (String)attribute.value;
                         } else if (attribute.localName.equals(INCLUDE_INLINE_ATTRIBUTE)) {
-                            inline = Boolean.parseBoolean(attribute.value);
+                            inline = Boolean.parseBoolean((String)attribute.value);
                         } else if (Character.isUpperCase(attribute.localName.charAt(0))) {
                             staticPropertyAttributes.add(attribute);
                         } else {
@@ -781,25 +834,6 @@ public class BXMLSerializer implements S
                     }
                 }
 
-                // Add the value to the context
-                if (element.id != null) {
-                    if (namespace.containsKey(element.id)) {
-                        throw new SerializationException("Element ID " + element.id
-                            + " is already in use.");
-                    }
-
-                    namespace.put(element.id, element.value);
-
-                    // If the type has an ID property, use it
-                    Class<?> type = element.value.getClass();
-                    IDProperty idProperty = type.getAnnotation(IDProperty.class);
-
-                    if (idProperty != null) {
-                        BeanAdapter beanAdapter = new BeanAdapter(element.value);
-                        beanAdapter.put(idProperty.value(), element.id);
-                    }
-                }
-
                 // Apply instance attributes
                 Dictionary<String, Object> dictionary;
                 if (element.value instanceof Dictionary<?, ?>) {
@@ -809,20 +843,7 @@ public class BXMLSerializer implements S
                 }
 
                 for (Attribute attribute : instancePropertyAttributes) {
-                    String sourcePath = getSourcePath(attribute.value);
-
-                    if (sourcePath == null) {
-                        dictionary.put(attribute.localName, resolve(attribute.value));
-                    } else {
-                        // Bind to <element id>.<attribute name>
-                        if (element.id == null) {
-                            throw new SerializationException("Bind target does not have an ID.");
-                        }
-
-                        String targetPath = element.id + "." + attribute.localName;
-                        NamespaceBinding namespaceBinding = new NamespaceBinding(namespace, sourcePath, targetPath);
-                        namespaceBinding.bind();
-                    }
+                    dictionary.put(attribute.localName, attribute.value);
                 }
 
                 // Apply static attributes
@@ -879,7 +900,7 @@ public class BXMLSerializer implements S
                             AttributeInvocationHandler handler =
                                 new AttributeInvocationHandler(scriptEngine,
                                     localNameComponents[1],
-                                    attribute.value);
+                                    (String)attribute.value);
 
                             Object listener = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                                 new Class<?>[]{propertyClass}, handler);
@@ -902,8 +923,7 @@ public class BXMLSerializer implements S
                             }
                         } else {
                             // The attribute represents a static setter
-                            setStaticProperty(element.value, propertyClass, localNameComponents[1],
-                                resolve(attribute.value));
+                            setStaticProperty(element.value, propertyClass, localNameComponents[1], attribute.value);
                         }
                     }
                 }
@@ -962,20 +982,7 @@ public class BXMLSerializer implements S
                             + " for read-only properties.");
                     }
 
-                    String sourcePath = getSourcePath(attribute.value);
-
-                    if (sourcePath == null) {
-                        dictionary.put(attribute.localName, resolve(attribute.value));
-                    } else {
-                        // Bind to <parent ID>.<element name>.<attribute name>
-                        if (element.parent.id == null) {
-                            throw new SerializationException("Bind target does not have an ID.");
-                        }
-
-                        String targetPath = element.parent.id + "." + element.localName + "." + attribute.localName;
-                        NamespaceBinding namespaceBinding = new NamespaceBinding(namespace, sourcePath, targetPath);
-                        namespaceBinding.bind();
-                    }
+                    dictionary.put(attribute.localName, attribute.value);
                 }
 
                 break;
@@ -1022,13 +1029,14 @@ public class BXMLSerializer implements S
 
             case SCRIPT: {
                 // Process attributes looking for src and language
+                // TODO Move to processStartElement(); we won't need to cast to String there
                 String src = null;
                 String language = this.language;
                 for (Attribute attribute : element.attributes) {
                     if (attribute.localName.equals(SCRIPT_SRC_ATTRIBUTE)) {
-                        src = attribute.value;
+                        src = (String)attribute.value;
                     } else if (attribute.localName.equals(SCRIPT_LANGUAGE_ATTRIBUTE)) {
-                        language = attribute.value;
+                        language = (String)attribute.value;
                     } else {
                         throw new SerializationException(attribute.localName + " is not a valid"
                             + " attribute for the " + internalNamespacePrefix + ":" + SCRIPT_TAG + " tag.");
@@ -1146,20 +1154,16 @@ public class BXMLSerializer implements S
         }
 
         // Move up the stack
-        if (element.parent != null) {
-            element = element.parent;
+        if (element.parent == null) {
+            root = element.value;
         }
+
+        element = element.parent;
     }
 
     private void logException() {
-        String message = "An error occurred while processing ";
-
-        if (element == null) {
-            message += " the root element";
-        } else {
-            message += " " + element.namespaceURI + "." + element.localName
-                + " starting at line number " + element.lineNumber;
-        }
+        Location streamReaderlocation = xmlStreamReader.getLocation();
+        String message = "An error occurred at line number " + streamReaderlocation.getLineNumber();
 
         if (location != null) {
             message += " in file " + location.getPath();
@@ -1328,73 +1332,6 @@ public class BXMLSerializer implements S
     }
 
     /**
-     * Resolves an attribute 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.
-     *
-     * @return
-     * The resolved value.
-     */
-    private Object resolve(String attributeValue)
-        throws MalformedURLException {
-        Object resolvedValue = null;
-
-        if (attributeValue.length() > 0) {
-            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("Resources is null.");
-                        }
-
-                        resolvedValue = JSON.get(resources, attributeValue.substring(1));
-
-                        if (resolvedValue == null) {
-                            resolvedValue = attributeValue;
-                        }
-                    }
-                }
-            } 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 = JSON.get(namespace, attributeValue.substring(1));
-
-                        if (resolvedValue == null) {
-                            resolvedValue = attributeValue;
-                        }
-                    }
-                }
-            } else {
-                resolvedValue = attributeValue;
-            }
-        } else {
-            resolvedValue = attributeValue;
-        }
-
-        return resolvedValue;
-    }
-
-    /**
      * Returns the file extension/MIME type map. This map associates file
      * extensions with MIME types, which are used to automatically determine
      * an appropriate serializer to use for an include based on file extension.
@@ -1524,20 +1461,4 @@ public class BXMLSerializer implements S
             throw new SerializationException(exception);
         }
     }
-
-    private static String getSourcePath(String attributeValue) {
-        int n = attributeValue.length();
-
-        String sourcePath;
-        if (n >= 3
-            && attributeValue.charAt(0) == OBJECT_REFERENCE_PREFIX
-            && attributeValue.charAt(1) == NAMESPACE_BINDING_PREFIX
-            && attributeValue.charAt(attributeValue.length() - 1) == NAMESPACE_BINDING_SUFFIX) {
-            sourcePath = attributeValue.substring(2, n - 1);
-        } else {
-            sourcePath = null;
-        }
-
-        return sourcePath;
-    }
 }