changeset 1782:174647284655 8.0-b58

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/8.0/MASTER/rt
author leifs
date Tue, 25 Sep 2012 13:06:21 -0700
parents 990e4f2d1a90 b6ba50d02d9e
children 7c436f8509c8 e5267f8c80af 07c0a2cbf8f3
files
diffstat 140 files changed, 24560 insertions(+), 749 deletions(-) [+]
line wrap: on
line diff
--- a/build.xml	Tue Sep 25 09:35:14 2012 -0700
+++ b/build.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -64,6 +64,7 @@
         <!--<ant antfile="${rt.root.dir}/javafx-designtime/build.xml" target="jar" inheritAll="false"/>-->
         <ant antfile="${rt.root.dir}/javafx-ui-controls/build.xml" target="jar" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="jar" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-fxml/build.xml" target="jar" inheritAll="false"/>
     </target>
 
     <!--
@@ -78,6 +79,7 @@
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-common/build.xml" target="test" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-util-converter/build.xml" target="test" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-fxml/build.xml" target="test" inheritAll="false"/>
     </target>
 
     <!--
@@ -94,6 +96,7 @@
         <ant antfile="${rt.root.dir}/javafx-ui-charts/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-ui-common/build.xml" target="clean" inheritAll="false"/>
         <ant antfile="${rt.root.dir}/javafx-util-converter/build.xml" target="clean" inheritAll="false"/>
+        <ant antfile="${rt.root.dir}/javafx-fxml/build.xml" target="clean" inheritAll="false"/>
     </target>
  
     <!--
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/.classpath	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" output="build/test/classes" path="test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/javafx-checkstyle"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/checkstyle"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-anim"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-beans"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-collections"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-logging"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-ui-common"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-common"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/junit"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-geom"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/javafx-sg-common"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/jfxrt"/>
+	<classpathentry kind="output" path="build/classes"/>
+</classpath>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/.hgignore	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,21 @@
+~$
+/build$
+/dist$
+/nbproject/private$
+/nbproject/sqe.properties$
+/nbproject/findbugs.settings$
+/nbproject/genfiles.properties$
+/test/(coverage|lib|results|work)$
+\.class$
+\.DS_Store$
+\.orig$
+\.rej$
+\.tmp\.$
+\.swp$
+^tags$
+/gensrc$
+\.ant-targets-build\.xml$
+^hs_err_pid[0-9]*\.log
+user\.build\.properties$
+/cobertura\.ser$
+junit.*\.properties$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/.project	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>javafx-fxml</name>
+	<comment></comment>
+	<projects>
+	</projects>
+  <buildSpec>
+    <buildCommand>
+      <name>org.eclipse.jdt.core.javabuilder</name>
+      <arguments>
+      </arguments>
+    </buildCommand>
+  </buildSpec>
+  <natures>
+    <nature>org.eclipse.jdt.core.javanature</nature>
+  </natures>
+</projectDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/build-closed.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-fxml" default="jar" basedir=".">
+    <description>Builds, tests, and jars the closed version of the javafx-fxml project.</description>
+    
+    <property name="runtime.dist.root.dir" value="../../rt-closed" />
+    <property name="rt.dist.root.dir" value="../../rt" />    
+    <path id="javac.closed.classpath.path" path="
+        ${runtime.dist.root.dir}/javafx-beans/dist/javafx-beans.jar:
+        ${runtime.dist.root.dir}/javafx-common/dist/javafx-common.jar:
+        ${rt.dist.root.dir}/javafx-ui-common/dist/javafx-ui-common.jar:
+        ${rt.dist.root.dir}/javafx-ui-controls/dist/javafx-ui-controls.jar:
+        ${rt.dist.root.dir}/javafx-ui-charts/dist/javafx-ui-charts.jar:
+        ${runtime.dist.root.dir}/javafx-ui-webnode/dist/javafx-ui-webnode.jar" />
+    <property name="javac.classpath" refid="javac.closed.classpath.path"/>
+    
+    <import file="../build-defs.xml"/>
+    <import file="build-common.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/build-common.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-fxml-common" default="jar" basedir=".">
+    <target name="trim-whitespace">
+        <fileset id="trimfiles" dir=".">
+            <include name="**/*.css"/>
+            <include name="**/*.html"/>
+            <include name="**/*.java"/>
+            <include name="**/*.js"/>
+            <include name="**/*.properties"/>
+            <include name="**/*.txt"/>
+            <include name="**/*.xml"/>
+        </fileset>
+        <replaceregexp match="[\t]" replace="    " flags="gm" byline="true">
+            <fileset refid="trimfiles"/>
+        </replaceregexp>
+        <replaceregexp match="[\t ]+$" replace="" flags="gm" byline="true">
+            <fileset refid="trimfiles"/>
+        </replaceregexp>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/build.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="javafx-fxml" default="jar" basedir=".">
+    <description>Builds, tests, and runs the OpenJFX javafx-fxml project.</description>
+
+    <import file="../build-defs.xml"/>
+    <import file="build-common.xml"/>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/nbproject/ide-file-targets.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project basedir=".." name="javafx-fxml-IDE">
+    <property file="../base.properties"/>
+    <property file="project.properties"/>
+    <property file="../common.properties"/>
+    <!-- TODO: edit the following target according to your needs -->
+    <!-- (more info: http://www.netbeans.org/kb/articles/freeform-config.html#runsingle) -->
+    <target name="debug-selected-file-in-test">
+        <fail unless="debug.class">Must set property 'debug.class'</fail>
+        <path id="cp">
+            <pathelement path="${javac.test.classpath}"/>
+            <pathelement location="${build.test.classes.dir}"/>
+        </path>
+        <nbjpdastart addressproperty="jpda.address" name="javafx-fxml" transport="dt_socket">
+            <classpath refid="cp"/>
+        </nbjpdastart>
+        <java classname="${debug.class}" fork="true">
+            <classpath refid="cp"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+        </java>
+    </target>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/nbproject/project.xml	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>javafx-fxml</name>
+            <properties>
+                <property-file>../base.properties</property-file>
+                <property-file>project.properties</property-file>
+                <property-file>../common.properties</property-file>
+            </properties>
+            <folders>
+                <source-folder>
+                    <label>Source Packages</label>
+                    <type>java</type>
+                    <location>${src.dir}</location>
+                </source-folder>
+                <source-folder>
+                    <label>Test Packages</label>
+                    <type>java</type>
+                    <location>${test.dir}</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>jar</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="test">
+                    <target>test</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>jar</target>
+                </action>
+                <action name="run.single">
+                    <target>test-single</target>
+                    <context>
+                        <property>run.file</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="test.single">
+                    <target>test-single</target>
+                    <context>
+                        <property>run.file</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>relative-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-file-in-test</target>
+                    <context>
+                        <property>debug.class</property>
+                        <folder>${test.dir}</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+            </ide-actions>
+            <export>
+                <type>jar</type>
+                <location>dist/javafx-fxml.jar</location>
+                <build-target>jar</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>${build.test.classes.dir}</location>
+                <build-target>jar</build-target>
+            </export>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>Source Packages</label>
+                        <location>${src.dir}</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>Test Packages</label>
+                        <location>${test.dir}</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                    <source-file>
+                        <location>project.properties</location>
+                    </source-file>
+                    <source-file>
+                        <location>nbproject/project.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="test"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
+            <compilation-unit>
+                <package-root>${src.dir}</package-root>
+                <classpath mode="compile">../javafx-logging/dist/javafx-logging.jar:../javafx-common/dist/javafx-common.jar:../javafx-beans/dist/javafx-beans.jar:../../rt/javafx-ui-common/dist/javafx-ui-common.jar:../../rt/javafx-ui-controls/dist/javafx-ui-controls.jar:../../rt/javafx-ui-charts/dist/javafx-ui-charts.jar:../javafx-ui-webnode/dist/javafx-ui-webnode.jar</classpath>
+                <built-to>dist/javafx-fxml.jar</built-to>
+                <source-level>1.6</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>${test.dir}</package-root>
+                <unit-tests/>
+                <classpath mode="compile">${javac.test.classpath}</classpath>
+                <built-to>${build.test.classes.dir}</built-to>
+                <source-level>1.6</source-level>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/project.properties	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,16 @@
+JFXRT_HOME=\
+    ${runtime.dist.root.dir}/../artifacts/sdk/rt/lib
+javac.classpath=\
+    ${JFXRT_HOME}/jfxrt.jar
+javac.test.classpath=\
+    ${javac.classpath}:\
+    ${build.classes.dir}:\
+    ${runtime.deps.dir}/junit.jar
+run.test.classpath=\
+    ${javac.test.classpath}:\
+    ${runtime.dist.root.dir}/../artifacts/sdk/rt/lib/jfxrt.jar:\
+    ${build.test.classes.dir}
+
+#test-sys-prop.javafx.toolkit=com.sun.javafx.pgstub.StubToolkit
+build.generated.dir=${build.dir}/gensrc
+disable-lombok=true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/BeanAdapter.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,948 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import java.lang.reflect.*;
+
+import javafx.beans.value.ObservableValue;
+
+/**
+ * Exposes Java Bean properties of an object via the {@link Map} interface.
+ * A call to {@link Map#get(Object)} invokes the getter for the corresponding
+ * property, and a call to {@link Map#put(Object, Object)} invokes the
+ * property's setter. Appending a "Property" suffix to the key returns the
+ * corresponding property model.
+ */
+public class BeanAdapter extends AbstractMap<String, Object> {
+    private Object bean;
+
+    private static HashMap<Class<?>, HashMap<String, LinkedList<Method>>> globalMethodCache =
+        new HashMap<Class<?>, HashMap<String, LinkedList<Method>>>();
+
+    public static final String GET_PREFIX = "get";
+    public static final String IS_PREFIX = "is";
+    public static final String SET_PREFIX = "set";
+    public static final String PROPERTY_SUFFIX = "Property";
+
+    public static final String VALUE_OF_METHOD_NAME = "valueOf";
+
+    /**
+     * Creates a new Bean adapter.
+     *
+     * @param bean
+     * The Bean object to wrap.
+     */
+    public BeanAdapter(Object bean) {
+        this.bean = bean;
+
+        Class<?> type = bean.getClass();
+
+        while (type != Object.class && !globalMethodCache.containsKey(type)) {
+            globalMethodCache.put(type, getClassMethodCache(type));
+            type = type.getSuperclass();
+        }
+    }
+
+    private static HashMap<String, LinkedList<Method>> getClassMethodCache(Class<?> type) {
+        HashMap<String, LinkedList<Method>> classMethodCache = new HashMap<String, LinkedList<Method>>();
+
+        Method[] declaredMethods = type.getDeclaredMethods();
+        for (int i = 0; i < declaredMethods.length; i++) {
+            Method method = declaredMethods[i];
+            int modifiers = method.getModifiers();
+
+            if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
+                String name = method.getName();
+                LinkedList<Method> namedMethods = classMethodCache.get(name);
+
+                if (namedMethods == null) {
+                    namedMethods = new LinkedList<Method>();
+                    classMethodCache.put(name, namedMethods);
+                }
+
+                namedMethods.add(method);
+            }
+        }
+
+        return classMethodCache;
+    }
+
+    /**
+     * Returns the Bean object this adapter wraps.
+     *
+     * @return
+     * The Bean object, or <tt>null</tt> if no Bean has been set.
+     */
+    public Object getBean() {
+        return bean;
+    }
+
+    private Method getMethod(String name, Class<?>... parameterTypes) {
+        Class<?> type = bean.getClass();
+
+        Method method = null;
+        while (type != Object.class) {
+            HashMap<String, LinkedList<Method>> classMethodCache = globalMethodCache.get(type);
+
+            LinkedList<Method> namedMethods = classMethodCache.get(name);
+            if (namedMethods != null) {
+                for (Method namedMethod : namedMethods) {
+                    if (namedMethod.getName().equals(name)
+                        && Arrays.equals(namedMethod.getParameterTypes(), parameterTypes)) {
+                        method = namedMethod;
+                        break;
+                    }
+                }
+            }
+
+            if (method != null) {
+                break;
+            }
+
+            type = type.getSuperclass();
+        }
+
+        return method;
+    }
+
+    private Method getGetterMethod(String key) {
+        Method getterMethod = getMethod(getMethodName(GET_PREFIX, key));
+
+        if (getterMethod == null) {
+            getterMethod = getMethod(getMethodName(IS_PREFIX, key));
+        }
+
+        return getterMethod;
+    }
+
+    private Method getSetterMethod(String key) {
+        Class<?> type = getType(key);
+
+        if (type == null) {
+            throw new UnsupportedOperationException("Cannot determine type for property.");
+        }
+
+        return getMethod(getMethodName(SET_PREFIX, key), type);
+    }
+
+    private static String getMethodName(String prefix, String key) {
+        return prefix + Character.toUpperCase(key.charAt(0)) + key.substring(1);
+    }
+
+    /**
+     * Invokes the getter method for the given property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @return
+     * The value returned by the method, or <tt>null</tt> if no such method
+     * exists.
+     */
+    @Override
+    public Object get(Object key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        return get(key.toString());
+    }
+
+    private Object get(String key) {
+        Method getterMethod = key.endsWith(PROPERTY_SUFFIX) ? getMethod(key) : getGetterMethod(key);
+
+        Object value;
+        if (getterMethod != null) {
+            try {
+                value = getterMethod.invoke(bean);
+            } catch (IllegalAccessException exception) {
+                throw new RuntimeException(exception);
+            } catch (InvocationTargetException exception) {
+                throw new RuntimeException(exception);
+            }
+        } else {
+            value = null;
+        }
+
+        return value;
+    }
+
+    /**
+     * Invokes a setter method for the given property. The
+     * {@link #coerce(Object, Class)} method is used as needed to attempt to
+     * convert a given value to the property type, as defined by the return
+     * value of the getter method.
+     *
+     * @param key
+     * The property name.
+     *
+     * @param value
+     * The new property value.
+     *
+     * @return
+     * Returns <tt>null</tt>, since returning the previous value would require
+     * an unnecessary call to the getter method.
+     *
+     * @throws PropertyNotFoundException
+     * If the given property does not exist or is read-only.
+     */
+    @Override
+    public Object put(String key, Object value) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        Method setterMethod = getSetterMethod(key);
+
+        if (setterMethod == null) {
+            throw new PropertyNotFoundException("Property \"" + key + "\" does not exist"
+                + " or is read-only.");
+        }
+
+        try {
+            setterMethod.invoke(bean, new Object[] {coerce(value, getType(key))});
+        } catch (IllegalAccessException exception) {
+            throw new RuntimeException(exception);
+        } catch (InvocationTargetException exception) {
+            throw new RuntimeException(exception);
+        }
+
+        return null;
+    }
+
+    /**
+     * Verifies the existence of a property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @return
+     * <tt>true</tt> if the property exists; <tt>false</tt>, otherwise.
+     */
+    @Override
+    public boolean containsKey(Object key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        return getType(key.toString()) != null;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Tests the mutability of a property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @return
+     * <tt>true</tt> if the property is read-only; <tt>false</tt>, otherwise.
+     */
+    public boolean isReadOnly(String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        return getSetterMethod(key) == null;
+    }
+
+    /**
+     * Returns the property model for the given property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @return
+     * The named property model, or <tt>null</tt> if no such property exists.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> ObservableValue<T> getPropertyModel(String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        return (ObservableValue<T>)get(key + BeanAdapter.PROPERTY_SUFFIX);
+    }
+
+    /**
+     * Returns the type of a property.
+     *
+     * @param key
+     * The property name.
+     */
+    public Class<?> getType(String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        Method getterMethod = getGetterMethod(key);
+
+        return (getterMethod == null) ? null : getterMethod.getReturnType();
+    }
+
+    /**
+     * Returns the generic type of a property.
+     *
+     * @param key
+     * The property name.
+     */
+    public Type getGenericType(String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        Method getterMethod = getGetterMethod(key);
+
+        return (getterMethod == null) ? null : getterMethod.getGenericReturnType();
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        boolean equals = false;
+
+        if (object instanceof BeanAdapter) {
+            BeanAdapter beanAdapter = (BeanAdapter)object;
+            equals = (bean == beanAdapter.bean);
+        }
+
+        return equals;
+    }
+
+    @Override
+    public int hashCode() {
+        return (bean == null) ? -1 : bean.hashCode();
+    }
+
+    /**
+     * Coerces a value to a given type.
+     *
+     * @param value
+     * @param type
+     *
+     * @return
+     * The coerced value.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T coerce(Object value, Class<? extends T> type) {
+        if (type == null) {
+            throw new NullPointerException();
+        }
+
+        Object coercedValue;
+
+        if (value == null) {
+            // Null values can only be coerced to null
+            coercedValue = null;
+        } else if (type.isAssignableFrom(value.getClass())) {
+            // Value doesn't require coercion
+            coercedValue = value;
+        } else if (type == Boolean.class
+            || type == Boolean.TYPE) {
+            coercedValue = Boolean.valueOf(value.toString());
+        } else if (type == Character.class
+            || type == Character.TYPE) {
+            coercedValue = value.toString().charAt(0);
+        } else if (type == Byte.class
+            || type == Byte.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).byteValue();
+            } else {
+                coercedValue = Byte.valueOf(value.toString());
+            }
+        } else if (type == Short.class
+            || type == Short.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).shortValue();
+            } else {
+                coercedValue = Short.valueOf(value.toString());
+            }
+        } else if (type == Integer.class
+            || type == Integer.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).intValue();
+            } else {
+                coercedValue = Integer.valueOf(value.toString());
+            }
+        } else if (type == Long.class
+            || type == Long.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).longValue();
+            } else {
+                coercedValue = Long.valueOf(value.toString());
+            }
+        } else if (type == BigInteger.class) {
+            if (value instanceof Number) {
+                coercedValue = BigInteger.valueOf(((Number)value).longValue());
+            } else {
+                coercedValue = new BigInteger(value.toString());
+            }
+        } else if (type == Float.class
+            || type == Float.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).floatValue();
+            } else {
+                coercedValue = Float.valueOf(value.toString());
+            }
+        } else if (type == Double.class
+            || type == Double.TYPE) {
+            if (value instanceof Number) {
+                coercedValue = ((Number)value).doubleValue();
+            } else {
+                coercedValue = Double.valueOf(value.toString());
+            }
+        } else if (type == Number.class) {
+            String number = value.toString();
+            if (number.contains(".")) {
+                coercedValue = Double.valueOf(number);
+            } else {
+                coercedValue = Long.valueOf(number);
+            }
+        } else if (type == BigDecimal.class) {
+            if (value instanceof Number) {
+                coercedValue = BigDecimal.valueOf(((Number)value).doubleValue());
+            } else {
+                coercedValue = new BigDecimal(value.toString());
+            }
+        } else if (type == Class.class) {
+            try {
+                coercedValue = Class.forName(value.toString());
+            } catch (ClassNotFoundException exception) {
+                throw new IllegalArgumentException(exception);
+            }
+        } else {
+            Class<?> valueType = value.getClass();
+            Method valueOfMethod = null;
+
+            while (valueOfMethod == null
+                && valueType != null) {
+                try {
+                    valueOfMethod = type.getDeclaredMethod(VALUE_OF_METHOD_NAME, valueType);
+                } catch (NoSuchMethodException exception) {
+                    // No-op
+                }
+
+                if (valueOfMethod == null) {
+                    valueType = valueType.getSuperclass();
+                }
+            }
+
+            if (valueOfMethod == null) {
+                throw new IllegalArgumentException("Unable to coerce " + value + " to " + type + ".");
+            }
+
+            if (type.isEnum()
+                && value instanceof String
+                && Character.isLowerCase(((String)value).charAt(0))) {
+                value = toAllCaps((String)value);
+            }
+
+            try {
+                coercedValue = valueOfMethod.invoke(null, value);
+            } catch (IllegalAccessException exception) {
+                throw new RuntimeException(exception);
+            } catch (InvocationTargetException exception) {
+                throw new RuntimeException(exception);
+            } catch (SecurityException exception) {
+                throw new RuntimeException(exception);
+            }
+        }
+
+        return (T)coercedValue;
+    }
+
+    /**
+     * Invokes the static getter method for the given property.
+     *
+     * @param target
+     * The object to which the property is attached.
+     *
+     * @param sourceType
+     * The class that defines the property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @return
+     * The value returned by the method, or <tt>null</tt> if no such method
+     * exists.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T get(Object target, Class<?> sourceType, String key) {
+        T value = null;
+
+        Class<?> targetType = target.getClass();
+        Method getterMethod = getStaticGetterMethod(sourceType, key, targetType);
+
+        if (getterMethod != null) {
+            try {
+                value = (T)getterMethod.invoke(null, target);
+            } catch (InvocationTargetException exception) {
+                throw new RuntimeException(exception);
+            } catch (IllegalAccessException exception) {
+                throw new RuntimeException(exception);
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * Invokes a static setter method for the given property. If the value is
+     * <tt>null</tt> or there is no explicit setter for a given type, the
+     * {@link #coerce(Object, Class)} method is used to attempt to convert the
+     * value to the actual property type (defined by the return value of the
+     * getter method).
+     *
+     * @param target
+     * The object to which the property is or will be attached.
+     *
+     * @param sourceType
+     * The class that defines the property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @param value
+     * The new property value.
+     *
+     * @throws PropertyNotFoundException
+     * If the given static property does not exist or is read-only.
+     */
+    public static void put(Object target, Class<?> sourceType, String key, Object value) {
+        Class<?> targetType = target.getClass();
+
+        Method setterMethod = null;
+        if (value != null) {
+            setterMethod = getStaticSetterMethod(sourceType, key, value.getClass(), targetType);
+        }
+
+        if (setterMethod == null) {
+            // Get the property type and attempt to coerce the value to it
+            Class<?> propertyType = getType(sourceType, key, targetType);
+
+            if (propertyType != null) {
+                setterMethod = getStaticSetterMethod(sourceType, key, propertyType, targetType);
+                value = coerce(value, propertyType);
+            }
+        }
+
+        if (setterMethod == null) {
+            throw new PropertyNotFoundException("Static property \"" + key + "\" does not exist"
+                + " or is read-only.");
+        }
+
+        // Invoke the setter
+        try {
+            setterMethod.invoke(null, target, value);
+        } catch (InvocationTargetException exception) {
+            throw new RuntimeException(exception);
+        } catch (IllegalAccessException exception) {
+            throw new RuntimeException(exception);
+        }
+    }
+
+    /**
+     * Tests the existence of a static property.
+     *
+     * @param sourceType
+     * The class that defines the property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @param targetType
+     * The type of the object to which the property applies.
+     *
+     * @return
+     * <tt>true</tt> if the property exists; <tt>false</tt>, otherwise.
+     */
+    public static boolean isDefined(Class<?> sourceType, String key, Class<?> targetType) {
+        return (getStaticGetterMethod(sourceType, key, targetType) != null);
+    }
+
+    /**
+     * Returns the type of a static property.
+     *
+     * @param sourceType
+     * The class that defines the property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @param targetType
+     * The type of the object to which the property applies.
+     */
+    public static Class<?> getType(Class<?> sourceType, String key, Class<?> targetType) {
+        Method getterMethod = getStaticGetterMethod(sourceType, key, targetType);
+        return (getterMethod == null) ? null : getterMethod.getReturnType();
+    }
+
+    /**
+     * Returns the generic type of a static property.
+     *
+     * @param sourceType
+     * The class that defines the property.
+     *
+     * @param key
+     * The property name.
+     *
+     * @param targetType
+     * The type of the object to which the property applies.
+     */
+    public static Type getGenericType(Class<?> sourceType, String key, Class<?> targetType) {
+        Method getterMethod = getStaticGetterMethod(sourceType, key, targetType);
+        return (getterMethod == null) ? null : getterMethod.getGenericReturnType();
+    }
+
+    /**
+     * Determines the type of a list item.
+     *
+     * @param listType
+     */
+    public static Class<?> getListItemType(Type listType) {
+        Type itemType = getGenericListItemType(listType);
+
+        if (itemType instanceof ParameterizedType) {
+            itemType = ((ParameterizedType)itemType).getRawType();
+        }
+
+        return (Class<?>)itemType;
+    }
+
+    /**
+     * Determines the type of a map value.
+     *
+     * @param listType
+     */
+    public static Class<?> getMapValueType(Type mapType) {
+        Type valueType = getGenericMapValueType(mapType);
+
+        if (valueType instanceof ParameterizedType) {
+            valueType = ((ParameterizedType)valueType).getRawType();
+        }
+
+        return (Class<?>)valueType;
+    }
+
+    /**
+     * Determines the type of a list item.
+     *
+     * @param listType
+     */
+    public static Type getGenericListItemType(Type listType) {
+        Type itemType = null;
+
+        Type parentType = listType;
+        while (parentType != null) {
+            if (parentType instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType)parentType;
+                Class<?> rawType = (Class<?>)parameterizedType.getRawType();
+
+                if (List.class.isAssignableFrom(rawType)) {
+                    itemType = parameterizedType.getActualTypeArguments()[0];
+                }
+
+                break;
+            }
+
+            Class<?> classType = (Class<?>)parentType;
+            Type[] genericInterfaces = classType.getGenericInterfaces();
+
+            for (int i = 0; i < genericInterfaces.length; i++) {
+                Type genericInterface = genericInterfaces[i];
+
+                if (genericInterface instanceof ParameterizedType) {
+                    ParameterizedType parameterizedType = (ParameterizedType)genericInterface;
+                    Class<?> interfaceType = (Class<?>)parameterizedType.getRawType();
+
+                    if (List.class.isAssignableFrom(interfaceType)) {
+                        itemType = parameterizedType.getActualTypeArguments()[0];
+                        break;
+                    }
+                }
+            }
+
+            if (itemType != null) {
+                break;
+            }
+
+            parentType = classType.getGenericSuperclass();
+        }
+
+        if (itemType != null && itemType instanceof TypeVariable<?>) {
+            itemType = Object.class;
+        }
+
+        return itemType;
+    }
+
+    /**
+     * Determines the type of a map value.
+     *
+     * @param mapType
+     */
+    public static Type getGenericMapValueType(Type mapType) {
+        Type valueType = null;
+
+        Type parentType = mapType;
+        while (parentType != null) {
+            if (parentType instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType)parentType;
+                Class<?> rawType = (Class<?>)parameterizedType.getRawType();
+
+                if (Map.class.isAssignableFrom(rawType)) {
+                    valueType = parameterizedType.getActualTypeArguments()[1];
+                }
+
+                break;
+            }
+
+            Class<?> classType = (Class<?>)parentType;
+            Type[] genericInterfaces = classType.getGenericInterfaces();
+
+            for (int i = 0; i < genericInterfaces.length; i++) {
+                Type genericInterface = genericInterfaces[i];
+
+                if (genericInterface instanceof ParameterizedType) {
+                    ParameterizedType parameterizedType = (ParameterizedType)genericInterface;
+                    Class<?> interfaceType = (Class<?>)parameterizedType.getRawType();
+
+                    if (Map.class.isAssignableFrom(interfaceType)) {
+                        valueType = parameterizedType.getActualTypeArguments()[1];
+                        break;
+                    }
+                }
+            }
+
+            if (valueType != null) {
+                break;
+            }
+
+            parentType = classType.getGenericSuperclass();
+        }
+
+        if (valueType != null && valueType instanceof TypeVariable<?>) {
+            valueType = Object.class;
+        }
+
+        return valueType;
+    }
+
+    /**
+     * Returns the value of a named constant.
+     *
+     * @param type
+     * The type that defines the constant.
+     *
+     * @param name
+     * The name of the constant.
+     */
+    public static Object getConstantValue(Class<?> type, String name) {
+        if (type == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (name == null) {
+            throw new IllegalArgumentException();
+        }
+
+        Field field;
+        try {
+            field = type.getField(name);
+        } catch (NoSuchFieldException exception) {
+            throw new IllegalArgumentException(exception);
+        }
+
+        int fieldModifiers = field.getModifiers();
+        if ((fieldModifiers & Modifier.STATIC) == 0
+            || (fieldModifiers & Modifier.FINAL) == 0) {
+            throw new IllegalArgumentException("Field is not a constant.");
+        }
+
+        Object value;
+        try {
+            value = field.get(null);
+        } catch (IllegalAccessException exception) {
+            throw new IllegalArgumentException(exception);
+        }
+
+        return value;
+    }
+
+    private static Method getStaticGetterMethod(Class<?> sourceType, String key,
+        Class<?> targetType) {
+        if (sourceType == null) {
+            throw new NullPointerException();
+        }
+
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        Method method = null;
+
+        if (targetType != null) {
+            key = Character.toUpperCase(key.charAt(0)) + key.substring(1);
+
+            String getMethodName = GET_PREFIX + key;
+            String isMethodName = IS_PREFIX + key;
+
+            try {
+                method = sourceType.getMethod(getMethodName, targetType);
+            } catch (NoSuchMethodException exception) {
+                // No-op
+            }
+
+            if (method == null) {
+                try {
+                    method = sourceType.getMethod(isMethodName, targetType);
+                } catch (NoSuchMethodException exception) {
+                    // No-op
+                }
+            }
+
+            // Check for interfaces
+            if (method == null) {
+                Class<?>[] interfaces = targetType.getInterfaces();
+                for (int i = 0; i < interfaces.length; i++) {
+                    try {
+                        method = sourceType.getMethod(getMethodName, interfaces[i]);
+                    } catch (NoSuchMethodException exception) {
+                        // No-op
+                    }
+
+                    if (method == null) {
+                        try {
+                            method = sourceType.getMethod(isMethodName, interfaces[i]);
+                        } catch (NoSuchMethodException exception) {
+                            // No-op
+                        }
+                    }
+
+                    if (method != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (method == null) {
+                method = getStaticGetterMethod(sourceType, key, targetType.getSuperclass());
+            }
+        }
+
+        return method;
+    }
+
+    private static Method getStaticSetterMethod(Class<?> sourceType, String key,
+        Class<?> valueType, Class<?> targetType) {
+        if (sourceType == null) {
+            throw new NullPointerException();
+        }
+
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        if (valueType == null) {
+            throw new NullPointerException();
+        }
+
+        Method method = null;
+
+        if (targetType != null) {
+            key = Character.toUpperCase(key.charAt(0)) + key.substring(1);
+
+            String setMethodName = SET_PREFIX + key;
+            try {
+                method = sourceType.getMethod(setMethodName, targetType, valueType);
+            } catch (NoSuchMethodException exception) {
+                // No-op
+            }
+
+            // Check for interfaces
+            if (method == null) {
+                Class<?>[] interfaces = targetType.getInterfaces();
+                for (int i = 0; i < interfaces.length; i++) {
+                    try {
+                        method = sourceType.getMethod(setMethodName, interfaces[i], valueType);
+                    } catch (NoSuchMethodException exception) {
+                        // No-op
+                    }
+
+                    if (method != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (method == null) {
+                method = getStaticSetterMethod(sourceType, key, valueType, targetType.getSuperclass());
+            }
+        }
+
+        return method;
+    }
+
+    private static String toAllCaps(String value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+
+        StringBuilder allCapsBuilder = new StringBuilder();
+
+        for (int i = 0, n = value.length(); i < n; i++) {
+            char c = value.charAt(i);
+
+            if (Character.isUpperCase(c)) {
+                allCapsBuilder.append('_');
+            }
+
+            allCapsBuilder.append(Character.toUpperCase(c));
+        }
+
+        return allCapsBuilder.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/LoadListener.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+/**
+ * Load listener interface.
+ */
+public interface LoadListener {
+    /**
+     * Called when the loader has read an import processing instruction.
+     *
+     * @param target
+     */
+    public void readImportProcessingInstruction(String target);
+
+    /**
+     * Called when the loader has read a language processing instruction.
+     *
+     * @param language
+     */
+    public void readLanguageProcessingInstruction(String language);
+
+    /**
+     * Called when the loader has read a comment.
+     *
+     * @param comment
+     */
+    public void readComment(String comment);
+
+    /**
+     * Called when the loader has begun reading an instance declaration
+     * element.
+     *
+     * @param type
+     */
+    public void beginInstanceDeclarationElement(Class<?> type);
+
+    /**
+     * Called when the loader has begun reading an instance declaration
+     * element for an unknown type.
+     *
+     * @param name
+     */
+    public void beginUnknownTypeElement(String name);
+
+    /**
+     * Called when the loader has begun reading an include element.
+     */
+    public void beginIncludeElement();
+
+    /**
+     * Called when the loader has begun reading a reference element.
+     */
+    public void beginReferenceElement();
+
+    /**
+     * Called when the loader has begun reading a copy element.
+     */
+    public void beginCopyElement();
+
+    /**
+     * Called when the loader has begun reading a root element.
+     */
+    public void beginRootElement();
+
+    /**
+     * Called when the loader has begun reading a property element.
+     *
+     * @param name
+     * @param sourceType
+     */
+    public void beginPropertyElement(String name, Class<?> sourceType);
+
+    /**
+     * Called when the loader has begun reading a static property element
+     * defined by an unknown type.
+     *
+     * @param name
+     * @param sourceType
+     */
+    public void beginUnknownStaticPropertyElement(String name);
+
+    /**
+     * Called when the loader has begun reading a script element.
+     */
+    public void beginScriptElement();
+
+    /**
+     * Called when the loader has begun reading a define element.
+     */
+    public void beginDefineElement();
+
+    /**
+     * Called when the loader has read an internal attribute.
+     *
+     * @param name
+     * @param value
+     */
+    public void readInternalAttribute(String name, String value);
+
+    /**
+     * Called when the loader has read a property attribute.
+     *
+     * @param name
+     * @param sourceType
+     * @param value
+     */
+    public void readPropertyAttribute(String name, Class<?> sourceType, String value);
+
+    /**
+     * Called when the loader has read an unknown static property attribute.
+     */
+    public void readUnknownStaticPropertyAttribute(String name, String value);
+
+    /**
+     * Called when the loader has read an event handler attribute.
+     *
+     * @param name
+     * @param value
+     */
+    public void readEventHandlerAttribute(String name, String value);
+
+    /**
+     * Called when the loader has finished reading an element.
+     *
+     * @param value
+     */
+    public void endElement(Object value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/ObservableListChangeEvent.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import javafx.event.Event;
+import javafx.event.EventType;
+
+/**
+ * Observable list change event.
+ */
+public class ObservableListChangeEvent<E> extends Event {
+    private static final long serialVersionUID = 0;
+
+    private EventType<ObservableListChangeEvent<?>> type;
+    private int from;
+    private int to;
+    private List<E> removed;
+
+    public static final EventType<ObservableListChangeEvent<?>> ADD =
+        new EventType<ObservableListChangeEvent<?>>(EventType.ROOT, ObservableListChangeEvent.class.getName() + "_ADD");
+    public static final EventType<ObservableListChangeEvent<?>> UPDATE =
+        new EventType<ObservableListChangeEvent<?>>(EventType.ROOT, ObservableListChangeEvent.class.getName() + "_UPDATE");
+    public static final EventType<ObservableListChangeEvent<?>> REMOVE =
+        new EventType<ObservableListChangeEvent<?>>(EventType.ROOT, ObservableListChangeEvent.class.getName() + "_REMOVE");
+
+    public ObservableListChangeEvent(ObservableList<E> source, EventType<ObservableListChangeEvent<?>> type,
+        int from, int to, List<E> removed) {
+        super(source, null, type);
+
+        this.from = from;
+        this.to = to;
+        this.removed = removed;
+    }
+
+    /**
+     * Returns the first index affected by the change.
+     */
+    public int getFrom() {
+        return from;
+    }
+
+    /**
+     * Returns the index immediately following the last index affected by the
+     * change.
+     */
+    public int getTo() {
+        return to;
+    }
+
+    /**
+     * Returns any elements that were removed as part of the change, or
+     * <tt>null</tt> if either this change did not cause any values to be
+     * removed or the removed values could not be determined.
+     */
+    public List<E> getRemoved() {
+        return removed;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + " " + type + ": [" + from + ".." + to + ") "
+            + ((removed == null) ? "" : removed);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/ObservableMapChangeEvent.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+import javafx.collections.ObservableMap;
+import javafx.event.Event;
+import javafx.event.EventType;
+
+/**
+ * Observable map change event.
+ */
+public class ObservableMapChangeEvent<K, V> extends Event {
+    private static final long serialVersionUID = 0;
+
+    private EventType<ObservableMapChangeEvent<?, ?>> type;
+    private K key;
+    private V removed;
+
+    public static final EventType<ObservableMapChangeEvent<?, ?>> ADD =
+        new EventType<ObservableMapChangeEvent<?, ?>>(EventType.ROOT, ObservableMapChangeEvent.class.getName() + "_ADD");
+    public static final EventType<ObservableMapChangeEvent<?, ?>> UPDATE =
+        new EventType<ObservableMapChangeEvent<?, ?>>(EventType.ROOT, ObservableMapChangeEvent.class.getName() + "_UPDATE");
+    public static final EventType<ObservableMapChangeEvent<?, ?>> REMOVE =
+        new EventType<ObservableMapChangeEvent<?, ?>>(EventType.ROOT, ObservableMapChangeEvent.class.getName() + "_REMOVE");
+
+    public ObservableMapChangeEvent(ObservableMap<K, V> source, EventType<ObservableMapChangeEvent<?, ?>> type,
+        K key, V removed) {
+        super(source, null, type);
+
+        this.type = type;
+        this.key = key;
+        this.removed = removed;
+    }
+
+    /**
+     * The key associated with the change.
+     */
+    public K getKey() {
+        return key;
+    }
+
+    /**
+     * They value that was removed as part of the change, or <tt>null</tt> if
+     * this change did not cause a value to be removed.
+     */
+    public V getRemoved() {
+        return removed;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName() + " " + type + ": " + key + " "
+            + ((removed == null) ? "" : removed);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/PropertyChangeEvent.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+import javafx.event.Event;
+import javafx.event.EventType;
+
+/**
+ * Property change event.
+ */
+public class PropertyChangeEvent<V> extends Event {
+    private static final long serialVersionUID = 0;
+
+    private V previousValue;
+
+    public static final EventType<PropertyChangeEvent<?>> PROPERTY_CHANGE =
+        new EventType<PropertyChangeEvent<?>>(EventType.ROOT, PropertyChangeEvent.class.getName() + "_PROPERTY_CHANGE");
+
+    public PropertyChangeEvent(Object source, V previousValue) {
+        super(source, null, PROPERTY_CHANGE);
+
+        this.previousValue = previousValue;
+    }
+
+    /**
+     * Returns the previous value of the property.
+     */
+    public V getPreviousValue() {
+        return previousValue;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/PropertyNotFoundException.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+/**
+ * Thrown when a caller attempts to set the value of a non-existent bean
+ * property.
+ */
+public class PropertyNotFoundException extends RuntimeException {
+    private static final long serialVersionUID = 0;
+
+    public PropertyNotFoundException() {
+        super();
+    }
+
+    public PropertyNotFoundException(String message) {
+        super(message);
+    }
+
+    public PropertyNotFoundException(Throwable cause) {
+        super(cause);
+    }
+
+    public PropertyNotFoundException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/builder/JavaFXFontBuilder.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.builder;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.AbstractMap;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javafx.scene.text.Font;
+import javafx.scene.text.FontPosture;
+import javafx.scene.text.FontWeight;
+import javafx.util.Builder;
+
+/**
+ * JavaFX font builder.
+ */
+public final class JavaFXFontBuilder extends AbstractMap<String, Object> implements Builder<Font> {
+    private String      name = null;
+    private double      size = 12D;
+    private FontWeight  weight = null;
+    private FontPosture posture = null;
+    private URL         url     = null;
+
+
+    @Override
+    public Font build() {
+        Font f;
+        if ( url != null) {
+            //TODO Implement some font name caching so that the font
+            // is not constructed from the stream every time
+            InputStream in = null;
+            try {
+                in = url.openStream();
+                f = Font.loadFont(in, size);
+            } catch( Exception e) {
+                //TODO
+                throw new RuntimeException( "Load of font file failed from " + url, e);
+            } finally {
+                try {
+                    if ( in != null) {
+                        in.close();
+                    }
+                } catch( Exception e) {
+                    //TODO
+                    e.printStackTrace();
+                }
+            }
+        } else {
+            if (weight == null && posture == null) {
+                f = new Font(name, size);
+            } else {
+                if (weight == null) weight = FontWeight.NORMAL;
+                if (posture == null) posture = FontPosture.REGULAR;
+                f = Font.font(name, weight, posture, size);
+            }
+        }
+        return f;
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        if ( "name".equals( key)) {
+            if ( value instanceof URL) {
+                url = (URL) value;
+            } else {
+                name = (String) value;
+            }
+        } else if ( "size".equals(key)) {
+            size =  Double.parseDouble((String) value);
+        } else if ( "style".equals(key)) {
+            String style = (String) value;
+            if ( style != null && style.length() > 0) {
+                boolean isWeightSet = false;
+                for( StringTokenizer st = new StringTokenizer( style, " "); st.hasMoreTokens(); ) {
+                    String stylePart = st.nextToken();
+                    FontWeight fw;
+                    if ( !isWeightSet && (fw=FontWeight.findByName(stylePart)) != null) {
+                        weight = fw;
+                        isWeightSet = true;
+                        continue;
+                    }
+                    FontPosture fp;
+                    if ( (fp=FontPosture.findByName(stylePart)) != null) {
+                        posture = fp;
+                        continue;
+                    }
+                }
+            }
+        } else if ( "url".equals(key)) {
+            if ( value instanceof URL) {
+                url = (URL) value;
+            } else {
+                try {
+                    url = new URL( value.toString());
+                } catch( MalformedURLException e) {
+                    //TODO Better exception
+                    throw new IllegalArgumentException("Invalid url " + value.toString(), e);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Unknown Font property: " + key);
+        }
+        return null;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/builder/JavaFXImageBuilder.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.builder;
+
+import java.util.AbstractMap;
+import java.util.Set;
+
+import javafx.scene.image.Image;
+import javafx.util.Builder;
+
+/**
+ * JavaFX image builder.
+ */
+public class JavaFXImageBuilder extends AbstractMap<String, Object> implements Builder<Image> {
+    private String      url = "";
+    private double      requestedWidth = 0;
+    private double      requestedHeight = 0;
+    private boolean     preserveRatio = false;
+    private boolean     smooth = false;
+    private boolean     backgroundLoading = false;
+
+    @Override
+    public Image build() {
+        return new Image( url, requestedWidth, requestedHeight, preserveRatio, smooth, backgroundLoading);
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        if ( value != null) {
+            String str = value.toString();
+
+            if ( "url".equals( key)) {
+                url = str;
+            } else if ( "requestedWidth".equals(key)) {
+                requestedWidth =  Double.parseDouble( str);
+            } else if ( "requestedHeight".equals(key)) {
+                requestedHeight =  Double.parseDouble(str);
+            } else if ( "preserveRatio".equals(key)) {
+                preserveRatio =  Boolean.parseBoolean(str);
+            } else if ( "smooth".equals(key)) {
+                smooth =  Boolean.parseBoolean(str);
+            } else if ( "backgroundLoading".equals(key)) {
+            } else {
+                throw new IllegalArgumentException("Unknown Image property: " + key);
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/builder/JavaFXSceneBuilder.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+import javafx.beans.DefaultProperty;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.util.Builder;
+
+/**
+ * JavaFX scene builder.
+ */
+@DefaultProperty("root")
+public class JavaFXSceneBuilder implements Builder<Scene> {
+    private Parent root = null;
+    private double width = -1;
+    private double height = -1;
+    private Paint fill = Color.WHITE;
+    private ArrayList<String> stylesheets = new ArrayList<String>();
+
+    public Parent getRoot() {
+        return root;
+    }
+
+    public void setRoot(Parent root) {
+        this.root = root;
+    }
+
+    public double getWidth() {
+        return width;
+    }
+
+    public void setWidth(double width) {
+        if (width < -1) {
+            throw new IllegalArgumentException();
+        }
+
+        this.width = width;
+    }
+
+    public double getHeight() {
+        return height;
+    }
+
+    public void setHeight(double height) {
+        if (height < -1) {
+            throw new IllegalArgumentException();
+        }
+
+        this.height = height;
+    }
+
+    public Paint getFill() {
+        return fill;
+    }
+
+    public void setFill(Paint fill) {
+        if (fill == null) {
+            throw new NullPointerException();
+        }
+
+        this.fill = fill;
+    }
+
+    public List<String> getStylesheets() {
+        return stylesheets;
+    }
+
+    @Override
+    public Scene build() {
+        Scene scene = new Scene(root, width, height, fill);
+
+        for (String stylesheet : stylesheets) {
+            scene.getStylesheets().add(stylesheet);
+        }
+
+        return scene;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/builder/URLBuilder.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.builder;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Set;
+import javafx.util.Builder;
+
+/**
+ * Builder for constructing URL instances.
+ */
+public class URLBuilder extends AbstractMap<String, Object> implements Builder<URL> {
+    private ClassLoader classLoader;
+
+    private Object value = null;
+
+    public static final String VALUE_KEY = "value";
+
+    public URLBuilder(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        if (key.equals(VALUE_KEY)) {
+            this.value = value;
+        } else {
+            throw new IllegalArgumentException(key + " is not a valid property.");
+        }
+
+        return null;
+    }
+
+    @Override
+    public URL build() {
+        if (value == null) {
+            throw new IllegalStateException();
+        }
+
+        URL url;
+        if (value instanceof URL) {
+            url = (URL)value;
+        } else {
+            String spec = value.toString();
+
+            if (spec.startsWith("/")) {
+                url = classLoader.getResource(spec);
+            } else {
+                try {
+                    url = new URL(spec);
+                } catch (MalformedURLException exception) {
+                    throw new RuntimeException(exception);
+                }
+            }
+        }
+
+        return url;
+    }
+
+    @Override
+    public Set<Map.Entry<String, Object>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/BinaryExpression.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.util.List;
+
+/**
+ * Abstract base class for binary expressions.
+ */
+public abstract class BinaryExpression extends Expression {
+    private Expression left;
+    private Expression right;
+
+    public BinaryExpression(Expression left, Expression right) {
+        if (left == null) {
+            throw new NullPointerException();
+        }
+
+        if (right == null) {
+            throw new NullPointerException();
+        }
+
+        this.left = left;
+        this.right = right;
+    }
+
+    public Expression getLeft() {
+        return left;
+    }
+
+    public Expression getRight() {
+        return right;
+    }
+
+    public abstract String getOperator();
+
+    @Override
+    public void update(Object namespace, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDefined(Object namespace) {
+        return left.isDefined(namespace) && right.isDefined(namespace);
+    }
+
+    @Override
+    public boolean isLValue() {
+        return false;
+    }
+
+    @Override
+    protected void getArguments(List<KeyPath> arguments) {
+        left.getArguments(arguments);
+        right.getArguments(arguments);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + left + " " + getOperator() + " " + right + ")";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/Expression.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,1659 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.javafx.fxml.BeanAdapter;
+
+/**
+ * Abstract base class for expressions. Also provides static methods for
+ * creating arithmetic and logical expressions as well as accessing namespace
+ * values by key path.
+ */
+public abstract class Expression {
+    // Expression parser class
+    private static class Parser {
+        public static class Token {
+            public Token(TokenType type, Object value) {
+                this.type = type;
+                this.value = value;
+            }
+
+            public final TokenType type;
+            public final Object value;
+
+            @Override
+            public String toString() {
+                return value.toString();
+            }
+        }
+
+        public enum TokenType {
+            LITERAL,
+            VARIABLE,
+            FUNCTION,
+            UNARY_OPERATOR,
+            BINARY_OPERATOR,
+            BEGIN_GROUP,
+            END_GROUP
+        }
+
+        private int c = -1;
+        private char[] pushbackBuffer = new char[PUSHBACK_BUFFER_SIZE];
+
+        private static final int MAX_PRIORITY = 6;
+        private static final int PUSHBACK_BUFFER_SIZE = 6;
+
+        public Expression parse(Reader reader) throws IOException {
+            LinkedList<Token> tokens = tokenize(new PushbackReader(reader, PUSHBACK_BUFFER_SIZE));
+
+            LinkedList<Expression> stack = new LinkedList<Expression>();
+
+            for (Token token : tokens) {
+                Expression expression;
+                switch (token.type) {
+                    case LITERAL: {
+                        expression = new LiteralExpression(token.value);
+                        break;
+                    }
+
+                    case VARIABLE: {
+                        expression = new VariableExpression((KeyPath)token.value);
+                        break;
+                    }
+
+                    case FUNCTION: {
+                        // TODO Create a new FunctionExpression type; this
+                        // class will have a property of type Method that
+                        // refers to a method defined by the "scope context"
+                        // (e.g. an FXML document controller), which must be
+                        // set prior to evaluating the expression; it will
+                        // also have a list of argument Expressions
+                        expression = null;
+                        break;
+                    }
+
+                    case UNARY_OPERATOR: {
+                        String operator = (String)token.value;
+                        Expression operand = stack.pop();
+
+                        if (operator.equals(NEGATE)) {
+                            expression = negate(operand);
+                        } else if (operator.equals(NOT)) {
+                            expression = not(operand);
+                        } else {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        break;
+                    }
+
+                    case BINARY_OPERATOR: {
+                        String operator = (String)token.value;
+                        Expression right = stack.pop();
+                        Expression left = stack.pop();
+
+                        if (operator.equals(ADD)) {
+                            expression = add(left, right);
+                        } else if (operator.equals(SUBTRACT)) {
+                            expression = subtract(left, right);
+                        } else if (operator.equals(MULTIPLY)) {
+                            expression = multiply(left, right);
+                        } else if (operator.equals(DIVIDE)) {
+                            expression = divide(left, right);
+                        } else if (operator.equals(MODULO)) {
+                            expression = modulo(left, right);
+                        } else if (operator.equals(GREATER_THAN)) {
+                            expression = greaterThan(left, right);
+                        } else if (operator.equals(GREATER_THAN_OR_EQUAL_TO)) {
+                            expression = greaterThanOrEqualTo(left, right);
+                        } else if (operator.equals(LESS_THAN)) {
+                            expression = lessThan(left, right);
+                        } else if (operator.equals(LESS_THAN_OR_EQUAL_TO)) {
+                            expression = lessThanOrEqualTo(left, right);
+                        } else if (operator.equals(EQUAL_TO)) {
+                            expression = equalTo(left, right);
+                        } else if (operator.equals(NOT_EQUAL_TO)) {
+                            expression = notEqualTo(left, right);
+                        } else if (operator.equals(AND)) {
+                            expression = and(left, right);
+                        } else if (operator.equals(OR)) {
+                            expression = or(left, right);
+                        } else {
+                            throw new UnsupportedOperationException();
+                        }
+
+                        break;
+                    }
+
+                    default: {
+                        throw new UnsupportedOperationException();
+                    }
+                }
+
+                stack.push(expression);
+            }
+
+            if (stack.size() != 1) {
+                throw new IllegalArgumentException("Invalid expression.");
+            }
+
+            return stack.peek();
+        }
+
+        private LinkedList<Token> tokenize(PushbackReader reader) throws IOException {
+            // Read the string into a postfix list of tokens
+            LinkedList<Token> tokens = new LinkedList<Token>();
+            LinkedList<Token> stack = new LinkedList<Token>();
+
+            c = reader.read();
+            boolean unary = true;
+
+            while (c != -1) {
+                // Skip whitespace
+                while (c != -1 && Character.isWhitespace(c)) {
+                    c = reader.read();
+                }
+
+                if (c != -1) {
+                    Token token;
+
+                    if (c == 'n') {
+                        if (readKeyword(reader, NULL_KEYWORD)) {
+                            token = new Token(TokenType.LITERAL, null);
+                        } else {
+                            token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
+                            c = reader.read();
+                        }
+                    } else if (c == '"' || c == '\'') {
+                        StringBuilder stringBuilder = new StringBuilder();
+
+                        // Use the same delimiter to close the string
+                        int t = c;
+
+                        // Move to the next character after the delimiter
+                        c = reader.read();
+
+                        while (c != -1 && c != t) {
+                            if (!Character.isISOControl(c)) {
+                                if (c == '\\') {
+                                    c = reader.read();
+
+                                    if (c == 'b') {
+                                        c = '\b';
+                                    } else if (c == 'f') {
+                                        c = '\f';
+                                    } else if (c == 'n') {
+                                        c = '\n';
+                                    } else if (c == 'r') {
+                                        c = '\r';
+                                    } else if (c == 't') {
+                                        c = '\t';
+                                    } else if (c == 'u') {
+                                        StringBuilder unicodeValueBuilder = new StringBuilder();
+                                        while (unicodeValueBuilder.length() < 4) {
+                                            c = reader.read();
+                                            unicodeValueBuilder.append((char)c);
+                                        }
+
+                                        String unicodeValue = unicodeValueBuilder.toString();
+                                        c = (char)Integer.parseInt(unicodeValue, 16);
+                                    } else {
+                                        if (!(c == '\\'
+                                            || c == '/'
+                                            || c == '\"'
+                                            || c == '\''
+                                            || c == t)) {
+                                            throw new IllegalArgumentException("Unsupported escape sequence.");
+                                        }
+                                    }
+                                }
+
+                                stringBuilder.append((char)c);
+                            }
+
+                            c = reader.read();
+                        }
+
+                        if (c != t) {
+                            throw new IllegalArgumentException("Unterminated string.");
+                        }
+
+                        // Move to the next character after the delimiter
+                        c = reader.read();
+
+                        token = new Token(TokenType.LITERAL, stringBuilder.toString());
+                    } else if (Character.isDigit(c)) {
+                        StringBuilder numberBuilder = new StringBuilder();
+                        boolean integer = true;
+
+                        while (c != -1 && (Character.isDigit(c) || c == '.'
+                            || c == 'e' || c == 'E')) {
+                            numberBuilder.append((char)c);
+                            integer &= !(c == '.');
+                            c = reader.read();
+                        }
+
+                        Number value;
+                        if (integer) {
+                            value = Long.parseLong(numberBuilder.toString());
+                        } else {
+                            value = Double.parseDouble(numberBuilder.toString());
+                        }
+
+                        token = new Token(TokenType.LITERAL, value);
+                    } else if (c == 't') {
+                        if (readKeyword(reader, TRUE_KEYWORD)) {
+                            token = new Token(TokenType.LITERAL, true);
+                        } else {
+                            token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
+                            c = reader.read();
+                        }
+                    } else if (c == 'f') {
+                        if (readKeyword(reader, FALSE_KEYWORD)) {
+                            token = new Token(TokenType.LITERAL, false);
+                        } else {
+                            token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
+                            c = reader.read();
+                        }
+                    } else if (Character.isJavaIdentifierStart(c)) {
+                        reader.unread(c);
+
+                        // TODO Here (and everywhere else where we call KeyPath.parse()),
+                        // read the path value. If c == '(' when this method returns, the
+                        // path refers to a function; read the arguments and create a
+                        // FUNCTION token
+                        token = new Token(TokenType.VARIABLE, KeyPath.parse(reader));
+                        c = reader.read();
+                    } else {
+                        if (c == NEGATE.charAt(0) && unary) {
+                            token = new Token(TokenType.UNARY_OPERATOR, NEGATE);
+                        } else if (c == NOT.charAt(0) && unary) {
+                            token = new Token(TokenType.UNARY_OPERATOR, NOT);
+                        } else if (c == ADD.charAt(0)) {
+                            token = new Token(TokenType.BINARY_OPERATOR, ADD);
+                        } else if (c == SUBTRACT.charAt(0)) {
+                            token = new Token(TokenType.BINARY_OPERATOR, SUBTRACT);
+                        } else if (c == MULTIPLY.charAt(0)) {
+                            token = new Token(TokenType.BINARY_OPERATOR, MULTIPLY);
+                        } else if (c == DIVIDE.charAt(0)) {
+                            token = new Token(TokenType.BINARY_OPERATOR, DIVIDE);
+                        } else if (c == MODULO.charAt(0)) {
+                            token = new Token(TokenType.BINARY_OPERATOR, MODULO);
+                        } else if (c == EQUAL_TO.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == EQUAL_TO.charAt(1)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, EQUAL_TO);
+                            } else {
+                                throw new IllegalArgumentException();
+                            }
+                        } else if (c == NOT_EQUAL_TO.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == NOT_EQUAL_TO.charAt(1)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, NOT_EQUAL_TO);
+                            } else {
+                                throw new IllegalArgumentException();
+                            }
+                        } else if (c == GREATER_THAN.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == GREATER_THAN_OR_EQUAL_TO.charAt(1)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, GREATER_THAN_OR_EQUAL_TO);
+                            } else {
+                                token = new Token(TokenType.BINARY_OPERATOR, GREATER_THAN);
+                            }
+                        } else if (c == LESS_THAN.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == LESS_THAN_OR_EQUAL_TO.charAt(1)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, LESS_THAN_OR_EQUAL_TO);
+                            } else {
+                                token = new Token(TokenType.BINARY_OPERATOR, LESS_THAN);
+                            }
+                        } else if (c == AND.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == AND.charAt(0)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, AND);
+                            } else {
+                                throw new IllegalArgumentException();
+                            }
+                        } else if (c == OR.charAt(0)) {
+                            c = reader.read();
+
+                            if (c == OR.charAt(0)) {
+                                token = new Token(TokenType.BINARY_OPERATOR, OR);
+                            } else {
+                                throw new IllegalArgumentException();
+                            }
+                        } else if (c == '(') {
+                            token = new Token(TokenType.BEGIN_GROUP, LEFT_PARENTHESIS);
+                        } else if (c == ')') {
+                            token = new Token(TokenType.END_GROUP, RIGHT_PARENTHESIS);
+                        } else {
+                            throw new IllegalArgumentException("Unexpected character in expression.");
+                        }
+
+                        c = reader.read();
+                    }
+
+                    // Process the token
+                    switch (token.type) {
+                        case LITERAL:
+                        case VARIABLE: {
+                            tokens.add(token);
+                            break;
+                        }
+
+                        case UNARY_OPERATOR:
+                        case BINARY_OPERATOR: {
+                            int priority = getPriority((String)token.value);
+
+                            while (!stack.isEmpty()
+                                && stack.peek().type != TokenType.BEGIN_GROUP
+                                && getPriority((String)stack.peek().value) >= priority
+                                && getPriority((String)stack.peek().value) != MAX_PRIORITY) {
+                                tokens.add(stack.pop());
+                            }
+
+                            stack.push(token);
+                            break;
+                        }
+
+                        case BEGIN_GROUP: {
+                            stack.push(token);
+                            break;
+                        }
+
+                        case END_GROUP: {
+                            for (token = stack.pop(); token.type != TokenType.BEGIN_GROUP; token = stack.pop()) {
+                                tokens.add(token);
+                            }
+
+                            break;
+                        }
+
+                        default: {
+                            throw new UnsupportedOperationException();
+                        }
+                    }
+
+                    unary = !(token.type == TokenType.LITERAL || token.type == TokenType.VARIABLE);
+                }
+            }
+
+            while (!stack.isEmpty()) {
+                tokens.add(stack.pop());
+            }
+
+            return tokens;
+        }
+
+        private boolean readKeyword(PushbackReader reader, String keyword) throws IOException {
+            int n = keyword.length();
+            int i = 0;
+
+            while (c != -1 && i < n) {
+                pushbackBuffer[i] = (char)c;
+                if (keyword.charAt(i) != c) {
+                    break;
+                }
+
+                c = reader.read();
+                i++;
+            }
+
+            boolean result;
+            if (i < n) {
+                reader.unread(pushbackBuffer, 0, i + 1);
+                result = false;
+            } else {
+                result = true;
+            }
+
+            return result;
+        }
+
+        private int getPriority(String operator) {
+            int priority;
+
+            if (operator.equals(NEGATE)
+                || operator.equals(NOT)) {
+                priority = MAX_PRIORITY;
+            } else if (operator.equals(MULTIPLY)
+                || operator.equals(DIVIDE)
+                || operator.equals(MODULO)) {
+                priority = MAX_PRIORITY - 1;
+            } else if (operator.equals(ADD)
+                || operator.equals(SUBTRACT)) {
+                priority = MAX_PRIORITY - 2;
+            } else if (operator.equals(GREATER_THAN)
+                || operator.equals(GREATER_THAN_OR_EQUAL_TO)
+                || operator.equals(LESS_THAN)
+                || operator.equals(LESS_THAN_OR_EQUAL_TO)) {
+                priority = MAX_PRIORITY - 3;
+            } else if (operator.equals(EQUAL_TO)
+                || operator.equals(NOT_EQUAL_TO)) {
+                priority = MAX_PRIORITY - 4;
+            } else if (operator.equals(AND)) {
+                priority = MAX_PRIORITY - 5;
+            } else if (operator.equals(OR)) {
+                priority = MAX_PRIORITY - 6;
+            } else {
+                throw new IllegalArgumentException();
+            }
+
+            return priority;
+        }
+    }
+
+    private static final String NEGATE = "-";
+    private static final String NOT = "!";
+
+    private static final String ADD = "+";
+    private static final String SUBTRACT = "-";
+    private static final String MULTIPLY = "*";
+    private static final String DIVIDE = "/";
+    private static final String MODULO = "%";
+
+    private static final String GREATER_THAN = ">";
+    private static final String GREATER_THAN_OR_EQUAL_TO = ">=";
+    private static final String LESS_THAN = "<";
+    private static final String LESS_THAN_OR_EQUAL_TO = "<=";
+    private static final String EQUAL_TO = "==";
+    private static final String NOT_EQUAL_TO = "!=";
+
+    private static final String AND = "&&";
+    private static final String OR = "||";
+
+    private static final String LEFT_PARENTHESIS = "(";
+    private static final String RIGHT_PARENTHESIS = ")";
+
+    private static final String NULL_KEYWORD = "null";
+    private static final String TRUE_KEYWORD = "true";
+    private static final String FALSE_KEYWORD = "false";
+
+    /**
+     * Evaluates the expression.
+     *
+     * @param namespace
+     * The namespace against which the expression will be evaluated.
+     *
+     * @return
+     * The result of evaluating the expression.
+     */
+    public abstract Object evaluate(Object namespace);
+
+    /**
+     * Updates the expression value.
+     *
+     * @param namespace
+     * The namespace against which the expression will be evaluated.
+     *
+     * @param value
+     * The value to assign to the expression.
+     */
+    public abstract void update(Object namespace, Object value);
+
+    /**
+     * Tests whether the expression is defined.
+     *
+     * @param namespace
+     * The namespace against which the expression will be evaluated.
+     *
+     * @return
+     * <tt>true</tt> if the expression is defined; <tt>false</tt>, otherwise.
+     */
+    public abstract boolean isDefined(Object namespace);
+
+    /**
+     * Tests whether the expression represents an l-value (i.e. can be
+     * assigned to).
+     *
+     * @return
+     * <tt>true</tt> if the expression is an l-value; <tt>false</tt>,
+     * otherwise.
+     */
+    public abstract boolean isLValue();
+
+    /**
+     * Returns a list of arguments to this expression.
+     */
+    public List<KeyPath> getArguments() {
+        ArrayList<KeyPath> arguments = new ArrayList<KeyPath>();
+        getArguments(arguments);
+
+        return arguments;
+    }
+
+    /**
+     * Populates a list of arguments to this expression.
+     */
+    protected abstract void getArguments(List<KeyPath> arguments);
+
+    /**
+     * Returns the value at a given path within a namespace.
+     *
+     * @param namespace
+     * @param keyPath
+     *
+     * @return
+     * The value at the given path, or <tt>null</tt> if no such value exists.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T get(Object namespace, KeyPath keyPath) {
+        if (keyPath == null) {
+            throw new NullPointerException();
+        }
+
+        return (T)get(namespace, keyPath.iterator());
+    }
+
+    /**
+     * Returns the value at a given path within a namespace.
+     *
+     * @param namespace
+     * @param keyPathIterator
+     *
+     * @return
+     * The value at the given path, or <tt>null</tt> if no such value exists.
+     */
+    @SuppressWarnings("unchecked")
+    private static <T> T get(Object namespace, Iterator<String> keyPathIterator) {
+        if (keyPathIterator == null) {
+            throw new NullPointerException();
+        }
+
+        T value;
+        if (keyPathIterator.hasNext()) {
+            // TODO Remove cast to T when build is updated to Java 7
+            value = (T)get(get(namespace, keyPathIterator.next()), keyPathIterator);
+        } else {
+            value = (T)namespace;
+        }
+
+        return value;
+    }
+
+    /**
+     * Returns the value at a given key within a namespace.
+     *
+     * @param namespace
+     * @param key
+     *
+     * @return
+     * The value at the given key, or <tt>null</tt> if no such value exists.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T get(Object namespace, String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        Object value;
+        if (namespace instanceof List<?>) {
+            List<Object> list = (List<Object>)namespace;
+            value = list.get(Integer.parseInt(key));
+        } else if (namespace != null) {
+            Map<String, Object> map;
+            if (namespace instanceof Map<?, ?>) {
+                map = (Map<String, Object>)namespace;
+            } else {
+                map = new BeanAdapter(namespace);
+            }
+
+            value = map.get(key);
+        } else {
+            value = null;
+        }
+
+        return (T)value;
+    }
+
+    /**
+     * Sets the value at a given path within a namespace.
+     *
+     * @param namespace
+     * @param keyPath
+     * @param value
+     */
+    public static void set(Object namespace, KeyPath keyPath, Object value) {
+        if (keyPath == null) {
+            throw new NullPointerException();
+        }
+
+        set(namespace, keyPath.iterator(), value);
+    }
+
+    /**
+     * Sets the value at a given path within a namespace.
+     *
+     * @param namespace
+     * @param keyPathIterator
+     * @param value
+     */
+    private static void set(Object namespace, Iterator<String> keyPathIterator, Object value) {
+        if (keyPathIterator == null) {
+            throw new NullPointerException();
+        }
+
+        if (!keyPathIterator.hasNext()) {
+            throw new IllegalArgumentException();
+        }
+
+        String key = keyPathIterator.next();
+
+        if (keyPathIterator.hasNext()) {
+            set(get(namespace, key), keyPathIterator, value);
+        } else {
+            set(namespace, key, value);
+        }
+    }
+
+    /**
+     * Sets the value at a given path within a namespace.
+     *
+     * @param namespace
+     * @param key
+     * @param value
+     */
+    @SuppressWarnings("unchecked")
+    public static void set(Object namespace, String key, Object value) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        if (namespace instanceof List<?>) {
+            List<Object> list = (List<Object>)namespace;
+            list.set(Integer.parseInt(key), value);
+        } else if (namespace != null) {
+            Map<String, Object> map;
+            if (namespace instanceof Map<?, ?>) {
+                map = (Map<String, Object>)namespace;
+            } else {
+                map = new BeanAdapter(namespace);
+            }
+
+            map.put(key, value);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Tests the existence of a path within a namespace.
+     *
+     * @param namespace
+     * @param keyPath
+     *
+     * @return
+     * <tt>true</tt> if the path exists; <tt>false</tt>, otherwise.
+     */
+    public static boolean isDefined(Object namespace, KeyPath keyPath) {
+        if (keyPath == null) {
+            throw new NullPointerException();
+        }
+
+        return isDefined(namespace, keyPath.iterator());
+    }
+
+    /**
+     * Tests the existence of a path within a namespace.
+     *
+     * @param namespace
+     * @param keyPathIterator
+     *
+     * @return
+     * <tt>true</tt> if the path exists; <tt>false</tt>, otherwise.
+     */
+    private static boolean isDefined(Object namespace, Iterator<String> keyPathIterator) {
+        if (keyPathIterator == null) {
+            throw new NullPointerException();
+        }
+
+        if (!keyPathIterator.hasNext()) {
+            throw new IllegalArgumentException();
+        }
+
+        String key = keyPathIterator.next();
+
+        boolean defined;
+        if (keyPathIterator.hasNext()) {
+            defined = isDefined(get(namespace, key), keyPathIterator);
+        } else {
+            defined = isDefined(namespace, key);
+        }
+
+        return defined;
+    }
+
+    /**
+     * Tests the existence of a key within a namespace.
+     *
+     * @param namespace
+     * @param key
+     *
+     * @return
+     * <tt>true</tt> if the key exists; <tt>false</tt>, otherwise.
+     */
+    @SuppressWarnings("unchecked")
+    public static boolean isDefined(Object namespace, String key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        boolean defined;
+        if (namespace instanceof List<?>) {
+            List<Object> list = (List<Object>)namespace;
+            defined = Integer.parseInt(key) < list.size();
+        } else if (namespace != null) {
+            Map<String, Object> map;
+            if (namespace instanceof Map<?, ?>) {
+                map = (Map<String, Object>)namespace;
+            } else {
+                map = new BeanAdapter(namespace);
+            }
+
+            defined = map.containsKey(key);
+        } else {
+            defined = false;
+        }
+
+        return defined;
+    }
+
+    /**
+     * Creates an addition or concatenation expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression add(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return ADD;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Object leftValue = getLeft().evaluate(namespace);
+                Object rightValue = getRight().evaluate(namespace);
+
+                Object value;
+                if (leftValue instanceof String || rightValue instanceof String) {
+                    value = leftValue.toString().concat(rightValue.toString());
+                } else {
+                    Number leftNumber = (Number)leftValue;
+                    Number rightNumber = (Number)rightValue;
+
+                    if (leftNumber instanceof Double || rightNumber instanceof Double) {
+                        value = leftNumber.doubleValue() + rightNumber.doubleValue();
+                    } else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+                        value = leftNumber.floatValue() + rightNumber.floatValue();
+                    } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+                        value = leftNumber.longValue() + rightNumber.longValue();
+                    } else if (leftNumber instanceof Integer || rightNumber instanceof Integer) {
+                        value = leftNumber.intValue() + rightNumber.intValue();
+                    } else if (leftNumber instanceof Short || rightNumber instanceof Short) {
+                        value = leftNumber.shortValue() + rightNumber.shortValue();
+                    } else if (leftNumber instanceof Byte || rightNumber instanceof Byte) {
+                        value = leftNumber.byteValue() + rightNumber.byteValue();
+                    } else {
+                        throw new UnsupportedOperationException();
+                    }
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates an addition or concatenation expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression add(Expression left, Object right) {
+        return add(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates an addition or concatenation expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression add(Object left, Expression right) {
+        return add(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates an addition or concatenation expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression add(Object left, Object right) {
+        return add(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a subtraction expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression subtract(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return SUBTRACT;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Number leftValue = (Number)getLeft().evaluate(namespace);
+                Number rightValue = (Number)getRight().evaluate(namespace);
+
+                Number value;
+                if (leftValue instanceof Double || rightValue instanceof Double) {
+                    value = leftValue.doubleValue() - rightValue.doubleValue();
+                } else if (leftValue instanceof Float || rightValue instanceof Float) {
+                    value = leftValue.floatValue() - rightValue.floatValue();
+                } else if (leftValue instanceof Long || rightValue instanceof Long) {
+                    value = leftValue.longValue() - rightValue.longValue();
+                } else if (leftValue instanceof Integer || rightValue instanceof Integer) {
+                    value = leftValue.intValue() - rightValue.intValue();
+                } else if (leftValue instanceof Short || rightValue instanceof Short) {
+                    value = leftValue.shortValue() - rightValue.shortValue();
+                } else if (leftValue instanceof Byte || rightValue instanceof Byte) {
+                    value = leftValue.byteValue() - rightValue.byteValue();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates a subtraction expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression subtract(Expression left, Number right) {
+        return subtract(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a subtraction expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression subtract(Number left, Expression right) {
+        return subtract(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a subtraction expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression subtract(Number left, Number right) {
+        return subtract(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a multiplication expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression multiply(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return MULTIPLY;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Number leftValue = (Number)getLeft().evaluate(namespace);
+                Number rightValue = (Number)getRight().evaluate(namespace);
+
+                Number value;
+                if (leftValue instanceof Double || rightValue instanceof Double) {
+                    value = leftValue.doubleValue() * rightValue.doubleValue();
+                } else if (leftValue instanceof Float || rightValue instanceof Float) {
+                    value = leftValue.floatValue() * rightValue.floatValue();
+                } else if (leftValue instanceof Long || rightValue instanceof Long) {
+                    value = leftValue.longValue() * rightValue.longValue();
+                } else if (leftValue instanceof Integer || rightValue instanceof Integer) {
+                    value = leftValue.intValue() * rightValue.intValue();
+                } else if (leftValue instanceof Short || rightValue instanceof Short) {
+                    value = leftValue.shortValue() * rightValue.shortValue();
+                } else if (leftValue instanceof Byte || rightValue instanceof Byte) {
+                    value = leftValue.byteValue() * rightValue.byteValue();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates a multiplication expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression multiply(Expression left, Number right) {
+        return multiply(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a multiplication expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression multiply(Number left, Expression right) {
+        return multiply(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a multiplication expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression multiply(Number left, Number right) {
+        return multiply(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a division expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression divide(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return DIVIDE;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Number leftValue = (Number)getLeft().evaluate(namespace);
+                Number rightValue = (Number)getRight().evaluate(namespace);
+
+                Number value;
+                if (leftValue instanceof Double || rightValue instanceof Double) {
+                    value = leftValue.doubleValue() / rightValue.doubleValue();
+                } else if (leftValue instanceof Float || rightValue instanceof Float) {
+                    value = leftValue.floatValue() / rightValue.floatValue();
+                } else if (leftValue instanceof Long || rightValue instanceof Long) {
+                    value = leftValue.longValue() / rightValue.longValue();
+                } else if (leftValue instanceof Integer || rightValue instanceof Integer) {
+                    value = leftValue.intValue() / rightValue.intValue();
+                } else if (leftValue instanceof Short || rightValue instanceof Short) {
+                    value = leftValue.shortValue() / rightValue.shortValue();
+                } else if (leftValue instanceof Byte || rightValue instanceof Byte) {
+                    value = leftValue.byteValue() / rightValue.byteValue();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates a division expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression divide(Expression left, Number right) {
+        return divide(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a division expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression divide(Number left, Expression right) {
+        return divide(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a division expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression divide(Number left, Number right) {
+        return divide(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a modulus expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression modulo(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return MODULO;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Number leftValue = (Number)getLeft().evaluate(namespace);
+                Number rightValue = (Number)getRight().evaluate(namespace);
+
+                Number value;
+                if (leftValue instanceof Double || rightValue instanceof Double) {
+                    value = leftValue.doubleValue() % rightValue.doubleValue();
+                } else if (leftValue instanceof Float || rightValue instanceof Float) {
+                    value = leftValue.floatValue() % rightValue.floatValue();
+                } else if (leftValue instanceof Long || rightValue instanceof Long) {
+                    value = leftValue.longValue() % rightValue.longValue();
+                } else if (leftValue instanceof Integer || rightValue instanceof Integer) {
+                    value = leftValue.intValue() % rightValue.intValue();
+                } else if (leftValue instanceof Short || rightValue instanceof Short) {
+                    value = leftValue.shortValue() % rightValue.shortValue();
+                } else if (leftValue instanceof Byte || rightValue instanceof Byte) {
+                    value = leftValue.byteValue() % rightValue.byteValue();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates a modulus expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression modulo(Expression left, Number right) {
+        return modulo(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a modulus expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression modulo(Number left, Expression right) {
+        return modulo(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a modulus expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression modulo(Number left, Number right) {
+        return modulo(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates an equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression equalTo(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return EQUAL_TO;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) == 0;
+            }
+        };
+    }
+
+    /**
+     * Creates an equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression equalTo(Expression left, Object right) {
+        return equalTo(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates an equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression equalTo(Object left, Expression right) {
+        return equalTo(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates an equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression equalTo(Object left, Object right) {
+        return equalTo(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates an inverse equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression notEqualTo(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return NOT_EQUAL_TO;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) != 0;
+            }
+        };
+    }
+
+    /**
+     * Creates an inverse equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression notEqualTo(Expression left, Object right) {
+        return notEqualTo(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates an inverse equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression notEqualTo(Object left, Expression right) {
+        return notEqualTo(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates an inverse equality expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression notEqualTo(Object left, Object right) {
+        return notEqualTo(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "greater-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThan(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return GREATER_THAN;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) > 0;
+            }
+        };
+    }
+
+    /**
+     * Creates a "greater-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThan(Expression left, Object right) {
+        return greaterThan(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "greater-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThan(Object left, Expression right) {
+        return greaterThan(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a "greater-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThan(Object left, Object right) {
+        return greaterThan(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "greater-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThanOrEqualTo(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return GREATER_THAN_OR_EQUAL_TO;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) >= 0;
+            }
+        };
+    }
+
+    /**
+     * Creates a "greater-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThanOrEqualTo(Expression left, Object right) {
+        return greaterThanOrEqualTo(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "greater-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThanOrEqualTo(Object left, Expression right) {
+        return greaterThanOrEqualTo(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a "greater-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression greaterThanOrEqualTo(Object left, Object right) {
+        return greaterThanOrEqualTo(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "less-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThan(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return LESS_THAN;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) < 0;
+            }
+        };
+    }
+
+    /**
+     * Creates a "less-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThan(Expression left, Object right) {
+        return lessThan(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "less-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThan(Object left, Expression right) {
+        return lessThan(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a "less-than" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThan(Object left, Object right) {
+        return lessThan(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "less-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThanOrEqualTo(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return LESS_THAN_OR_EQUAL_TO;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public Object evaluate(Object namespace) {
+                return ((Comparable<Object>)getLeft().evaluate(namespace)).compareTo(getRight().evaluate(namespace)) <= 0;
+            }
+        };
+    }
+
+    /**
+     * Creates a "less-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThanOrEqualTo(Expression left, Object right) {
+        return lessThanOrEqualTo(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a "less-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThanOrEqualTo(Object left, Expression right) {
+        return lessThanOrEqualTo(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a "less-than-or-equal-to" comparison expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression lessThanOrEqualTo(Object left, Object right) {
+        return lessThanOrEqualTo(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a boolean "and" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression and(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return AND;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                return (Boolean)getLeft().evaluate(namespace) && (Boolean)getRight().evaluate(namespace);
+            }
+        };
+    }
+
+    /**
+     * Creates a boolean "and" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression and(Expression left, Boolean right) {
+        return and(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a boolean "and" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression and(Boolean left, Expression right) {
+        return and(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a boolean "and" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression and(Boolean left, Boolean right) {
+        return and(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a boolean "or" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression or(Expression left, Expression right) {
+        return new BinaryExpression(left, right) {
+            @Override
+            public String getOperator() {
+                return OR;
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                return (Boolean)getLeft().evaluate(namespace) || (Boolean)getRight().evaluate(namespace);
+            }
+        };
+    }
+
+    /**
+     * Creates a boolean "or" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression or(Expression left, Boolean right) {
+        return or(left, new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a boolean "or" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression or(Boolean left, Expression right) {
+        return or(new LiteralExpression(left), right);
+    }
+
+    /**
+     * Creates a boolean "or" expression.
+     *
+     * @param left
+     * @param right
+     */
+    public static BinaryExpression or(Boolean left, Boolean right) {
+        return or(new LiteralExpression(left), new LiteralExpression(right));
+    }
+
+    /**
+     * Creates a numeric negation expression.
+     *
+     * @param operand
+     */
+    public static UnaryExpression negate(Expression operand) {
+        return new UnaryExpression(operand) {
+            @Override
+            public String getOperator() {
+                return "-";
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                Number value = (Number)getOperand().evaluate(namespace);
+                Class<? extends Number> type = value.getClass();
+
+                if (type == Byte.class) {
+                    value = -value.byteValue();
+                } else if (type == Short.class) {
+                    value = -value.shortValue();
+                } else if (type == Integer.class) {
+                    value = -value.intValue();
+                } else if (type == Long.class) {
+                    value = -value.longValue();
+                } else if (type == Float.class) {
+                    value = -value.floatValue();
+                } else if (type == Double.class) {
+                    value = -value.doubleValue();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                return value;
+            }
+        };
+    }
+
+    /**
+     * Creates a numeric negation expression.
+     *
+     * @param operand
+     */
+    public UnaryExpression negate(Number operand) {
+        return negate(new LiteralExpression(operand));
+    }
+
+    /**
+     * Creates a boolean "not" expression.
+     *
+     * @param operand
+     */
+    public static UnaryExpression not(Expression operand) {
+        return new UnaryExpression(operand) {
+            @Override
+            public String getOperator() {
+                return "!";
+            }
+
+            @Override
+            public Object evaluate(Object namespace) {
+                return !(Boolean)getOperand().evaluate(namespace);
+            }
+        };
+    }
+
+    /**
+     * Creates a boolean "not" expression.
+     *
+     * @param operand
+     */
+    public static UnaryExpression not(Boolean operand) {
+        return not(new LiteralExpression(operand));
+    }
+
+    /**
+     * Parses a string representation of an expression into an expression
+     * tree.
+     *
+     * @param value
+     * The string representation of the expression.
+     */
+    public static Expression valueOf(String value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+
+        Parser parser = new Parser();
+        Expression expression;
+        try {
+            expression = parser.parse(new StringReader(value));
+        } catch (IOException exception) {
+            throw new RuntimeException(exception);
+        }
+
+        return expression;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/ExpressionValue.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.beans.InvalidationListener;
+import javafx.beans.property.ReadOnlyProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.beans.value.ObservableValueBase;
+import javafx.collections.ListChangeListener;
+import javafx.collections.MapChangeListener;
+import javafx.collections.ObservableList;
+import javafx.collections.ObservableMap;
+
+import com.sun.javafx.fxml.BeanAdapter;
+
+/**
+ * Class representing an observable expression value.
+ */
+public class ExpressionValue extends ObservableValueBase<Object> {
+    // Monitors a namespace for changes along a key path
+    private class KeyPathMonitor {
+        private String key;
+        private KeyPathMonitor next;
+
+        private Object namespace = null;
+
+        private ListChangeListener<Object> listChangeListener = new ListChangeListener<Object>() {
+            @Override
+            public void onChanged(Change<? extends Object> change) {
+                while (change.next()) {
+                    int index = Integer.parseInt(key);
+
+                    if (index >= change.getFrom() && index < change.getTo()) {
+                        fireValueChangedEvent();
+                        remonitor();
+                    }
+                }
+            }
+        };
+
+        private MapChangeListener<String, Object> mapChangeListener = new MapChangeListener<String, Object>() {
+            @Override
+            public void onChanged(Change<? extends String, ? extends Object> change) {
+                if (key.equals(change.getKey())) {
+                    fireValueChangedEvent();
+                    remonitor();
+                }
+            }
+        };
+
+        private ChangeListener<Object> propertyChangeListener = new ChangeListener<Object>() {
+            @Override
+            public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
+                ReadOnlyProperty<?> property = (ReadOnlyProperty<?>)observable;
+
+                if (key.equals(property.getName())) {
+                    fireValueChangedEvent();
+                    remonitor();
+                }
+            }
+        };
+
+        public KeyPathMonitor(Iterator<String> keyPathIterator) {
+            this.key = keyPathIterator.next();
+
+            if (keyPathIterator.hasNext()) {
+                next = new KeyPathMonitor(keyPathIterator);
+            } else {
+                next = null;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void monitor(Object namespace) {
+            if (namespace instanceof ObservableList<?>) {
+                ((ObservableList<Object>)namespace).addListener(listChangeListener);
+            } else if (namespace instanceof ObservableMap<?, ?>) {
+                ((ObservableMap<String, Object>)namespace).addListener(mapChangeListener);
+            } else {
+                BeanAdapter namespaceAdapter = new BeanAdapter(namespace);
+                ObservableValue<Object> propertyModel = namespaceAdapter.getPropertyModel(key);
+
+                if (propertyModel != null) {
+                    propertyModel.addListener(propertyChangeListener);
+                }
+
+                namespace = namespaceAdapter;
+            }
+
+            this.namespace = namespace;
+
+            if (next != null) {
+                next.monitor(Expression.get(namespace, key));
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void unmonitor() {
+            if (namespace instanceof ObservableList<?>) {
+                ((ObservableList<Object>)namespace).removeListener(listChangeListener);
+            } else if (namespace instanceof ObservableMap<?, ?>) {
+                ((ObservableMap<String, Object>)namespace).removeListener(mapChangeListener);
+            } else {
+                BeanAdapter namespaceAdapter = (BeanAdapter)namespace;
+                ObservableValue<Object> propertyModel = namespaceAdapter.getPropertyModel(key);
+
+                if (propertyModel != null) {
+                    propertyModel.removeListener(propertyChangeListener);
+                }
+            }
+
+            namespace = null;
+
+            if (next != null) {
+                next.unmonitor();
+            }
+        }
+
+        public void remonitor() {
+            if (next != null) {
+                next.unmonitor();
+                next.monitor(Expression.get(namespace, key));
+            }
+        }
+    }
+
+    private Object namespace;
+    private Expression expression;
+    private Class<?> type;
+
+    private ArrayList<KeyPathMonitor> argumentMonitors;
+
+    private int listenerCount = 0;
+
+    public ExpressionValue(Object namespace, Expression expression, Class<?> type) {
+        if (namespace == null) {
+            throw new NullPointerException();
+        }
+
+        if (expression == null) {
+            throw new NullPointerException();
+        }
+
+        if (type == null) {
+            throw new NullPointerException();
+        }
+
+        this.namespace = namespace;
+        this.expression = expression;
+        this.type = type;
+
+        List<KeyPath> arguments = expression.getArguments();
+        argumentMonitors = new ArrayList<KeyPathMonitor>(arguments.size());
+
+        for (KeyPath argument : arguments) {
+            argumentMonitors.add(new KeyPathMonitor(argument.iterator()));
+        }
+    }
+
+    @Override
+    public Object getValue() {
+        return BeanAdapter.coerce(expression.evaluate(namespace), type);
+    }
+
+    @Override
+    public void addListener(InvalidationListener listener) {
+        if (listenerCount == 0) {
+            monitorArguments();
+        }
+
+        super.addListener(listener);
+        listenerCount++;
+    }
+
+    @Override
+    public void removeListener(InvalidationListener listener) {
+        super.removeListener(listener);
+        listenerCount--;
+
+        if (listenerCount == 0) {
+            unmonitorArguments();
+        }
+    }
+
+    @Override
+    public void addListener(ChangeListener<? super Object> listener) {
+        if (listenerCount == 0) {
+            monitorArguments();
+        }
+
+        super.addListener(listener);
+        listenerCount++;
+    }
+
+    @Override
+    public void removeListener(ChangeListener<? super Object> listener) {
+        super.removeListener(listener);
+        listenerCount--;
+
+        if (listenerCount == 0) {
+            unmonitorArguments();
+        }
+    }
+
+    private void monitorArguments() {
+        for (KeyPathMonitor argumentMonitor : argumentMonitors) {
+            argumentMonitor.monitor(namespace);
+        }
+    }
+
+    private void unmonitorArguments() {
+        for (KeyPathMonitor argumentMonitor : argumentMonitors) {
+            argumentMonitor.unmonitor();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/KeyPath.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.StringReader;
+import java.util.AbstractList;
+import java.util.ArrayList;
+
+/**
+ * Class representing a key path, an immutable list of string keys.
+ */
+public class KeyPath extends AbstractList<String> {
+    private ArrayList<String> elements;
+
+    public KeyPath(ArrayList<String> elements) {
+        if (elements == null) {
+            throw new NullPointerException();
+        }
+
+        this.elements = elements;
+    }
+
+    @Override
+    public String get(int index) {
+        return elements.get(index);
+    }
+
+    @Override
+    public int size() {
+        return elements.size();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder();
+
+        for (int i = 0, n = elements.size(); i < n; i++) {
+            String element = elements.get(i);
+
+            if (Character.isDigit(element.charAt(0))) {
+                stringBuilder.append("[");
+                stringBuilder.append(element);
+                stringBuilder.append("]");
+            } else {
+                if (i > 0) {
+                    stringBuilder.append(".");
+                }
+
+                stringBuilder.append(element);
+            }
+        }
+
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Parses a string value into a key path.
+     *
+     * @param value
+     * The string value to parse.
+     *
+     * @return
+     * The resulting key path.
+     */
+    public static KeyPath parse(String value) {
+        KeyPath keyPath;
+        try {
+            PushbackReader reader = new PushbackReader(new StringReader(value));
+
+            try {
+                keyPath = parse(reader);
+            } finally {
+                reader.close();
+            }
+        } catch (IOException exception) {
+            throw new RuntimeException(exception);
+        }
+
+        return keyPath;
+    }
+
+    /**
+     * Parses character content from a reader into a key path. If the character
+     * following the key path is not EOF, it is pushed back.
+     *
+     * @param reader
+     * The reader containing the content to parse.
+     *
+     * @return
+     * The resulting key path.
+     */
+    protected static KeyPath parse(PushbackReader reader) throws IOException {
+        ArrayList<String> elements = new ArrayList<String>();
+
+        int c = reader.read();
+
+        while (c != -1 && (Character.isJavaIdentifierStart(c) || c == '[')) {
+            StringBuilder keyBuilder = new StringBuilder();
+
+            boolean bracketed = (c == '[');
+
+            if (bracketed) {
+                c = reader.read();
+                boolean quoted = (c == '"' || c == '\'');
+
+                char quote;
+                if (quoted) {
+                    quote = (char)c;
+                    c = reader.read();
+                } else {
+                    quote = Character.UNASSIGNED;
+                }
+
+                while (c != -1
+                    && bracketed) {
+                    if (Character.isISOControl(c)) {
+                        throw new IllegalArgumentException("Illegal identifier character.");
+                    }
+
+                    if (!quoted
+                        && !Character.isDigit(c)) {
+                        throw new IllegalArgumentException("Illegal character in index value.");
+                    }
+
+                    keyBuilder.append((char)c);
+                    c = reader.read();
+
+                    if (quoted) {
+                        quoted = c != quote;
+
+                        if (!quoted) {
+                            c = reader.read();
+                        }
+                    }
+
+                    bracketed = c != ']';
+                }
+
+                if (quoted) {
+                    throw new IllegalArgumentException("Unterminated quoted identifier.");
+                }
+
+                if (bracketed) {
+                    throw new IllegalArgumentException("Unterminated bracketed identifier.");
+                }
+
+                c = reader.read();
+            } else {
+                while(c != -1 && (c != '.' && c != '[' && Character.isJavaIdentifierPart(c))) {
+                    keyBuilder.append((char)c);
+                    c = reader.read();
+                }
+            }
+
+            if (c == '.') {
+                c = reader.read();
+
+                if (c == -1) {
+                    throw new IllegalArgumentException("Illegal terminator character.");
+                }
+            }
+
+            if (keyBuilder.length() == 0) {
+                throw new IllegalArgumentException("Missing identifier.");
+            }
+
+            elements.add(keyBuilder.toString());
+        }
+
+        if (elements.size() == 0) {
+            throw new IllegalArgumentException("Invalid path.");
+        }
+
+        // Can't push back EOF; subsequent calls to read() should still return -1
+        if (c != -1) {
+            reader.unread(c);
+        }
+
+        return new KeyPath(elements);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/LiteralExpression.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.util.List;
+
+/**
+ * Expression representing a literal value.
+ */
+public class LiteralExpression extends Expression {
+    private Object value;
+
+    public LiteralExpression(Object value) {
+        this.value = value;
+    }
+
+    @Override
+    public Object evaluate(Object namespace) {
+        return value;
+    }
+
+    @Override
+    public void update(Object namespace, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDefined(Object namespace) {
+        return true;
+    }
+
+    @Override
+    public boolean isLValue() {
+        return false;
+    }
+
+    @Override
+    protected void getArguments(List<KeyPath> arguments) {
+        // No-op
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/UnaryExpression.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.util.List;
+
+/**
+ * Abstract base class for unary expressions.
+ */
+public abstract class UnaryExpression extends Expression {
+    private Expression operand;
+
+    public UnaryExpression(Expression operand) {
+        if (operand == null) {
+            throw new NullPointerException();
+        }
+
+        this.operand = operand;
+    }
+
+    public Expression getOperand() {
+        return operand;
+    }
+
+    public abstract String getOperator();
+
+    @Override
+    public void update(Object namespace, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDefined(Object namespace) {
+        return operand.isDefined(namespace);
+    }
+
+    @Override
+    public boolean isLValue() {
+        return false;
+    }
+
+    @Override
+    protected void getArguments(List<KeyPath> arguments) {
+        operand.getArguments(arguments);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + getOperator() + operand + ")";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/com/sun/javafx/fxml/expression/VariableExpression.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml.expression;
+
+import java.util.List;
+
+/**
+ * Expression representing a variable value.
+ */
+public class VariableExpression extends Expression {
+    private KeyPath keyPath;
+
+    public VariableExpression(KeyPath keyPath) {
+        if (keyPath == null) {
+            throw new NullPointerException();
+        }
+
+        this.keyPath = keyPath;
+    }
+
+    /**
+     * Returns the path to the variable associated with this expression.
+     */
+    public KeyPath getKeyPath() {
+        return keyPath;
+    }
+
+    @Override
+    public Object evaluate(Object namespace) {
+        return get(namespace, keyPath);
+    }
+
+    @Override
+    public void update(Object namespace, Object value) {
+        set(namespace, keyPath, value);
+    }
+
+    @Override
+    public boolean isDefined(Object namespace) {
+        return isDefined(namespace, keyPath);
+    }
+
+    @Override
+    public boolean isLValue() {
+        return true;
+    }
+
+    @Override
+    protected void getArguments(List<KeyPath> arguments) {
+        arguments.add(keyPath);
+    }
+
+    @Override
+    public String toString() {
+        return keyPath.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/FXML.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that tags a class or member as accessible to markup.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface FXML {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/FXMLLoader.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,2727 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javafx.beans.DefaultProperty;
+import javafx.beans.property.Property;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.MapChangeListener;
+import javafx.collections.ObservableList;
+import javafx.collections.ObservableMap;
+import javafx.event.Event;
+import javafx.event.EventHandler;
+import javafx.event.EventType;
+import javafx.util.Builder;
+import javafx.util.BuilderFactory;
+import javafx.util.Callback;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.util.StreamReaderDelegate;
+
+import com.sun.javafx.beans.IDProperty;
+import com.sun.javafx.fxml.BeanAdapter;
+import com.sun.javafx.fxml.LoadListener;
+import com.sun.javafx.fxml.ObservableListChangeEvent;
+import com.sun.javafx.fxml.ObservableMapChangeEvent;
+import com.sun.javafx.fxml.PropertyChangeEvent;
+import com.sun.javafx.fxml.PropertyNotFoundException;
+import com.sun.javafx.fxml.expression.Expression;
+import com.sun.javafx.fxml.expression.ExpressionValue;
+import com.sun.javafx.fxml.expression.KeyPath;
+
+/**
+ * Loads an object hierarchy from an XML document.
+ */
+public class FXMLLoader {
+    // Abstract base class for elements
+    private abstract class Element {
+        public final Element parent;
+        public final int lineNumber;
+
+        public Object value = null;
+        private BeanAdapter valueAdapter = null;
+
+        public final LinkedList<Attribute> eventHandlerAttributes = new LinkedList<Attribute>();
+        public final LinkedList<Attribute> instancePropertyAttributes = new LinkedList<Attribute>();
+        public final LinkedList<Attribute> staticPropertyAttributes = new LinkedList<Attribute>();
+        public final LinkedList<PropertyElement> staticPropertyElements = new LinkedList<PropertyElement>();
+
+        public Element() {
+            parent = current;
+            lineNumber = getLineNumber();
+        }
+
+        public boolean isCollection() {
+            // Return true if value is a list, or if the value's type defines
+            // a default property that is a list
+            boolean collection;
+            if (value instanceof List<?>) {
+                collection = true;
+            } else {
+                Class<?> type = value.getClass();
+                DefaultProperty defaultProperty = type.getAnnotation(DefaultProperty.class);
+
+                if (defaultProperty != null) {
+                    collection = getProperties().get(defaultProperty.value()) instanceof List<?>;
+                } else {
+                    collection = false;
+                }
+            }
+
+            return collection;
+        }
+
+        @SuppressWarnings("unchecked")
+        public void add(Object element) throws LoadException {
+            // If value is a list, add element to it; otherwise, get the value
+            // of the default property, which is assumed to be a list and add
+            // to that (coerce to the appropriate type)
+            List<Object> list;
+            if (value instanceof List<?>) {
+                list = (List<Object>)value;
+            } else {
+                Class<?> type = value.getClass();
+                DefaultProperty defaultProperty = type.getAnnotation(DefaultProperty.class);
+                String defaultPropertyName = defaultProperty.value();
+
+                // Get the list value
+                list = (List<Object>)getProperties().get(defaultPropertyName);
+
+                // Coerce the element to the list item type
+                if (!Map.class.isAssignableFrom(type)) {
+                    Type listType = getValueAdapter().getGenericType(defaultPropertyName);
+                    element = BeanAdapter.coerce(element, BeanAdapter.getListItemType(listType));
+                }
+            }
+
+            list.add(element);
+        }
+
+        public void set(Object value) throws LoadException {
+            if (this.value == null) {
+                throw new LoadException("Cannot set value on this element.");
+            }
+
+            // Apply value to this element's properties
+            Class<?> type = this.value.getClass();
+            DefaultProperty defaultProperty = type.getAnnotation(DefaultProperty.class);
+            if (defaultProperty == null) {
+                throw new LoadException("Element does not define a default property.");
+            }
+
+            getProperties().put(defaultProperty.value(), value);
+        }
+
+        public void updateValue(Object value) {
+            this.value = value;
+            valueAdapter = null;
+        }
+
+        public boolean isTyped() {
+            return !(value instanceof Map<?, ?>);
+        }
+
+        public BeanAdapter getValueAdapter() {
+            if (valueAdapter == null) {
+                valueAdapter = new BeanAdapter(value);
+            }
+
+            return valueAdapter;
+        }
+
+        @SuppressWarnings("unchecked")
+        public Map<String, Object> getProperties() {
+            return (isTyped()) ? getValueAdapter() : (Map<String, Object>)value;
+        }
+
+        public void processStartElement() throws IOException {
+            for (int i = 0, n = xmlStreamReader.getAttributeCount(); i < n; i++) {
+                String prefix = xmlStreamReader.getAttributePrefix(i);
+                String localName = xmlStreamReader.getAttributeLocalName(i);
+                String value = xmlStreamReader.getAttributeValue(i);
+
+                if (loadListener != null
+                    && prefix != null
+                    && prefix.equals(FX_NAMESPACE_PREFIX)) {
+                    loadListener.readInternalAttribute(prefix + ":" + localName, value);
+                }
+
+                processAttribute(prefix, localName, value);
+            }
+        }
+
+        public void processEndElement() throws IOException {
+            // No-op
+        }
+
+        public void processCharacters() throws IOException {
+            throw new LoadException("Unexpected characters in input stream.");
+        }
+
+        public void processInstancePropertyAttributes() throws IOException {
+            if (instancePropertyAttributes.size() > 0) {
+                for (Attribute attribute : instancePropertyAttributes) {
+                    processPropertyAttribute(attribute);
+                }
+            }
+        }
+
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException{
+            if (prefix == null) {
+                // Add the attribute to the appropriate list
+                if (localName.startsWith(EVENT_HANDLER_PREFIX)) {
+                    if (loadListener != null) {
+                        loadListener.readEventHandlerAttribute(localName, value);
+                    }
+
+                    eventHandlerAttributes.add(new Attribute(localName, null, value));
+                } else {
+                    int i = localName.lastIndexOf('.');
+
+                    if (i == -1) {
+                        // The attribute represents an instance property
+                        if (loadListener != null) {
+                            loadListener.readPropertyAttribute(localName, null, value);
+                        }
+
+                        instancePropertyAttributes.add(new Attribute(localName, null, value));
+                    } else {
+                        // The attribute represents a static property
+                        String name = localName.substring(i + 1);
+                        Class<?> sourceType = getType(localName.substring(0, i));
+
+                        if (sourceType != null) {
+                            if (loadListener != null) {
+                                loadListener.readPropertyAttribute(name, sourceType, value);
+                            }
+
+                            staticPropertyAttributes.add(new Attribute(name, sourceType, value));
+                        } else if (staticLoad) {
+                            if (loadListener != null) {
+                                loadListener.readUnknownStaticPropertyAttribute(localName, value);
+                            }
+                        } else {
+                            throw new LoadException(localName + " is not a valid attribute.");
+                        }
+                    }
+
+                }
+            } else {
+                throw new LoadException(prefix + ":" + localName
+                    + " is not a valid attribute.");
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void processPropertyAttribute(Attribute attribute) throws IOException {
+            String value = attribute.value;
+
+            if (value.startsWith(ESCAPE_PREFIX)) {
+                value = value.substring(ESCAPE_PREFIX.length());
+
+                if (value.length() == 0
+                    || !(value.startsWith(ESCAPE_PREFIX)
+                        || value.startsWith(RELATIVE_PATH_PREFIX)
+                        || value.startsWith(RESOURCE_KEY_PREFIX)
+                        || value.startsWith(EXPRESSION_PREFIX)
+                        || value.startsWith(BI_DIRECTIONAL_BINDING_PREFIX))) {
+                    throw new LoadException("Invalid escape sequence.");
+                }
+
+                applyProperty(attribute.name, attribute.sourceType, value);
+            } else if (value.startsWith(RELATIVE_PATH_PREFIX)) {
+                value = value.substring(RELATIVE_PATH_PREFIX.length());
+
+                if (value.startsWith(RELATIVE_PATH_PREFIX)) {
+                    // The prefix was escaped
+                    warnDeprecatedEscapeSequence(RELATIVE_PATH_PREFIX);
+                    applyProperty(attribute.name, attribute.sourceType, value);
+                } else {
+                    if (value.length() == 0) {
+                        throw new LoadException("Missing relative path.");
+                    }
+
+                    URL location;
+                    if (value.charAt(0) == '/') {
+                        location = classLoader.getResource(value.substring(1));
+                    } else {
+                        if (FXMLLoader.this.location == null) {
+                            throw new LoadException("Base location is undefined.");
+                        }
+
+                        location = new URL(FXMLLoader.this.location, value);
+                    }
+
+                    applyProperty(attribute.name, attribute.sourceType, location);
+                }
+            } else if (value.startsWith(RESOURCE_KEY_PREFIX)) {
+                value = value.substring(RESOURCE_KEY_PREFIX.length());
+
+                if (value.startsWith(RESOURCE_KEY_PREFIX)) {
+                    // The prefix was escaped
+                    warnDeprecatedEscapeSequence(RESOURCE_KEY_PREFIX);
+                    applyProperty(attribute.name, attribute.sourceType, value);
+                } else {
+                    if (value.length() == 0) {
+                        throw new LoadException("Missing resource key.");
+                    }
+
+                    // Resolve the resource value
+                    if (resources == null) {
+                        throw new LoadException("No resources specified.");
+                    }
+
+                    if (!resources.containsKey(value)) {
+                        throw new LoadException("Resource \"" + value + "\" not found.");
+                    }
+
+                    applyProperty(attribute.name, attribute.sourceType, resources.getObject(value));
+                }
+            } else if (value.startsWith(EXPRESSION_PREFIX)) {
+                value = value.substring(EXPRESSION_PREFIX.length());
+
+                if (value.startsWith(EXPRESSION_PREFIX)) {
+                    // The prefix was escaped
+                    warnDeprecatedEscapeSequence(EXPRESSION_PREFIX);
+                    applyProperty(attribute.name, attribute.sourceType, value);
+                } else if (value.equals(NULL_KEYWORD)) {
+                    // The attribute value is null
+                    applyProperty(attribute.name, attribute.sourceType, null);
+                } else {
+                    if (value.length() == 0) {
+                        throw new LoadException("Missing expression.");
+                    }
+
+                    // Resolve the expression
+                    Expression expression;
+                    if (value.startsWith(BINDING_EXPRESSION_PREFIX)
+                        && value.endsWith(BINDING_EXPRESSION_SUFFIX)) {
+                        if (attribute.sourceType != null) {
+                            throw new LoadException("Cannot bind to static property.");
+                        }
+
+                        if (!isTyped()) {
+                            throw new LoadException("Cannot bind to untyped object.");
+                        }
+
+                        // TODO We may want to identify binding properties in processAttribute()
+                        // and apply them after build() has been called
+                        if (this.value instanceof Builder) {
+                            throw new LoadException("Cannot bind to builder property.");
+                        }
+
+                        value = value.substring(1, value.length() - 1);
+                        expression = Expression.valueOf(value);
+
+                        // Create the binding
+                        BeanAdapter targetAdapter = new BeanAdapter(this.value);
+                        ObservableValue<Object> propertyModel = targetAdapter.getPropertyModel(attribute.name);
+                        Class<?> type = targetAdapter.getType(attribute.name);
+
+                        if (propertyModel instanceof Property<?>) {
+                            ((Property<Object>)propertyModel).bind(new ExpressionValue(namespace, expression, type));
+                        }
+                    } else {
+                        applyProperty(attribute.name, attribute.sourceType, Expression.get(namespace, KeyPath.parse(value)));
+                    }
+                }
+            } else if (value.startsWith(BI_DIRECTIONAL_BINDING_PREFIX)) {
+                throw new UnsupportedOperationException("This feature is not currently enabled.");
+            } else {
+                Object propertyValue = value;
+
+                if (attribute.sourceType == null && isTyped()) {
+                    BeanAdapter valueAdapter = getValueAdapter();
+                    Class<?> type = valueAdapter.getType(attribute.name);
+
+                    if (type == null) {
+                        throw new PropertyNotFoundException("Property \"" + attribute.name
+                            + "\" does not exist" + " or is read-only.");
+                    }
+
+                    if (List.class.isAssignableFrom(type)
+                        && valueAdapter.isReadOnly(attribute.name)) {
+                        // Split the string and add the values to the list
+                        List<Object> list = (List<Object>)valueAdapter.get(attribute.name);
+                        Type listType = valueAdapter.getGenericType(attribute.name);
+                        Type itemType = (Class<?>)BeanAdapter.getGenericListItemType(listType);
+
+                        if (itemType instanceof ParameterizedType) {
+                            itemType = ((ParameterizedType)itemType).getRawType();
+                        }
+
+                        String stringValue = value.toString();
+                        if (stringValue.length() > 0) {
+                            String[] values = stringValue.split(ARRAY_COMPONENT_DELIMITER);
+
+                            for (int i = 0; i < values.length; i++) {
+                                list.add(BeanAdapter.coerce(values[i].trim(), (Class<?>)itemType));
+                            }
+                        }
+
+                        propertyValue = null;
+                    } else if (type.isArray()) {
+                        // Split the string and set the values as an array
+                        Class<?> componentType = type.getComponentType();
+
+                        String stringValue = value.toString();
+                        if (stringValue.length() > 0) {
+                            String[] values = stringValue.split(ARRAY_COMPONENT_DELIMITER);
+                            propertyValue = Array.newInstance(componentType, values.length);
+                            for (int i = 0; i < values.length; i++) {
+                                Array.set(propertyValue, i, BeanAdapter.coerce(values[i].trim(),
+                                    type.getComponentType()));
+                            }
+                        } else {
+                            propertyValue = Array.newInstance(componentType, 0);
+                        }
+                    }
+                }
+
+                if (propertyValue != null) {
+                    applyProperty(attribute.name, attribute.sourceType, propertyValue);
+                }
+            }
+        }
+
+        public void warnDeprecatedEscapeSequence(String prefix) {
+            System.err.println(prefix + prefix + " is a deprecated escape sequence. "
+                + "Please use \\" + prefix + " instead.");
+        }
+
+        public void applyProperty(String name, Class<?> sourceType, Object value) {
+            if (sourceType == null) {
+                getProperties().put(name, value);
+            } else {
+                BeanAdapter.put(this.value, sourceType, name, value);
+            }
+        }
+
+        public void processEventHandlerAttributes() throws LoadException {
+            if (eventHandlerAttributes.size() > 0 && !staticLoad) {
+                for (Attribute attribute : eventHandlerAttributes) {
+                    EventHandler<? extends Event> eventHandler = null;
+
+                    String value = attribute.value;
+
+                    if (value.startsWith(CONTROLLER_METHOD_PREFIX)) {
+                        value = value.substring(CONTROLLER_METHOD_PREFIX.length());
+
+                        if (!value.startsWith(CONTROLLER_METHOD_PREFIX)) {
+                            if (value.length() == 0) {
+                                throw new LoadException("Missing controller method.");
+                            }
+
+                            if (controller == null) {
+                                throw new LoadException("No controller specified.");
+                            }
+
+                            Method method = getControllerMethods().get(value);
+
+                            if (method == null) {
+                                throw new LoadException("Controller method \"" + value + "\" not found.");
+                            }
+
+                            eventHandler = new ControllerMethodEventHandler(controller, method);
+                        }
+                    }
+
+                    if (eventHandler == null) {
+                        if (value.length() == 0) {
+                            throw new LoadException("Missing handler script.");
+                        }
+
+                        if (scriptEngine == null) {
+                            throw new LoadException("Page language not specified.");
+                        }
+
+                        eventHandler = new ScriptEventHandler(value, scriptEngine);
+                    }
+
+                    // Add the handler
+                    if (eventHandler != null){
+                        addEventHandler(attribute, eventHandler);
+                    }
+                }
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private void addEventHandler(Attribute attribute, EventHandler<? extends Event> eventHandler)
+            throws LoadException {
+            if (attribute.name.endsWith(CHANGE_EVENT_HANDLER_SUFFIX)) {
+                int i = EVENT_HANDLER_PREFIX.length();
+                int j = attribute.name.length() - CHANGE_EVENT_HANDLER_SUFFIX.length();
+
+                if (i == j) {
+                    if (value instanceof ObservableList<?>) {
+                        ObservableList<Object> list = (ObservableList<Object>)value;
+                        list.addListener(new ObservableListChangeAdapter(list,
+                            (EventHandler<ObservableListChangeEvent<?>>)eventHandler));
+                    } else if (value instanceof ObservableMap<?, ?>) {
+                        ObservableMap<Object, Object> map = (ObservableMap<Object, Object>)value;
+                        map.addListener(new ObservableMapChangeAdapter(map,
+                            (EventHandler<ObservableMapChangeEvent<?, ?>>)eventHandler));
+                    } else {
+                        throw new LoadException("Invalid event source.");
+                    }
+                } else {
+                    String key = Character.toLowerCase(attribute.name.charAt(i))
+                        + attribute.name.substring(i + 1, j);
+
+                    ObservableValue<Object> propertyModel = getValueAdapter().getPropertyModel(key);
+                    if (propertyModel == null) {
+                        throw new LoadException(value.getClass().getName() + " does not define"
+                                + " a property model for \"" + key + "\".");
+                    }
+
+                    propertyModel.addListener(new PropertyChangeAdapter(value,
+                        (EventHandler<PropertyChangeEvent<?>>)eventHandler));
+                }
+            } else {
+                getValueAdapter().put(attribute.name, eventHandler);
+            }
+        }
+    }
+
+    // Element representing a value
+    private abstract class ValueElement extends Element {
+        public String id = null;
+
+        @Override
+        public void processStartElement() throws IOException {
+            super.processStartElement();
+
+            updateValue(constructValue());
+
+            if (value instanceof Builder<?>) {
+                processInstancePropertyAttributes();
+            } else {
+                processValue();
+            }
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public void processEndElement() throws IOException {
+            super.processEndElement();
+
+            // Build the value, if necessary
+            if (value instanceof Builder<?>) {
+                Builder<Object> builder = (Builder<Object>)value;
+                updateValue(builder.build());
+
+                processValue();
+            } else {
+                processInstancePropertyAttributes();
+            }
+
+            processEventHandlerAttributes();
+
+            // Process static property attributes
+            if (staticPropertyAttributes.size() > 0) {
+                for (Attribute attribute : staticPropertyAttributes) {
+                    processPropertyAttribute(attribute);
+                }
+            }
+
+            // Process static property elements
+            if (staticPropertyElements.size() > 0) {
+                for (PropertyElement element : staticPropertyElements) {
+                    BeanAdapter.put(value, element.sourceType, element.name, element.value);
+                }
+            }
+
+            if (parent != null) {
+                if (parent.isCollection()) {
+                    parent.add(value);
+                } else {
+                    parent.set(value);
+                }
+            }
+        }
+
+        private Object getListValue(Element parent, String listPropertyName, Object value) {
+            // If possible, coerce the value to the list item type
+            if (parent.isTyped()) {
+                Type listType = parent.getValueAdapter().getGenericType(listPropertyName);
+
+                if (listType != null) {
+                    Type itemType = BeanAdapter.getGenericListItemType(listType);
+
+                    if (itemType instanceof ParameterizedType) {
+                        itemType = ((ParameterizedType)itemType).getRawType();
+                    }
+
+                    value = BeanAdapter.coerce(value, (Class<?>)itemType);
+                }
+            }
+
+            return value;
+        }
+
+        private void processValue() throws LoadException {
+            // If this is the root element, update the value
+            if (parent == null) {
+                root = value;
+            }
+
+            // Add the value to the namespace
+            if (id != null) {
+                namespace.put(id, value);
+
+                // Set the controller field value
+                if (controller != null) {
+                    Field field = getControllerFields().get(id);
+
+                    if (field != null) {
+                        try {
+                            field.set(controller, value);
+                        } catch (IllegalAccessException exception) {
+                            throw new RuntimeException(exception);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public void processCharacters() throws LoadException {
+            Class<?> type = value.getClass();
+            DefaultProperty defaultProperty = type.getAnnotation(DefaultProperty.class);
+
+            // If the default property is a read-only list, add the value to it;
+            // otherwise, set the value as the default property
+            if (defaultProperty != null) {
+                String text = xmlStreamReader.getText();
+                text = extraneousWhitespacePattern.matcher(text).replaceAll(" ");
+
+                String defaultPropertyName = defaultProperty.value();
+                BeanAdapter valueAdapter = getValueAdapter();
+
+                if (valueAdapter.isReadOnly(defaultPropertyName)
+                    && List.class.isAssignableFrom(valueAdapter.getType(defaultPropertyName))) {
+                    List<Object> list = (List<Object>)valueAdapter.get(defaultPropertyName);
+                    list.add(getListValue(this, defaultPropertyName, text));
+                } else {
+                    valueAdapter.put(defaultPropertyName, text.trim());
+                }
+            } else {
+                throw new LoadException(type.getName() + " does not have a default property.");
+            }
+        }
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException{
+            if (prefix != null
+                && prefix.equals(FX_NAMESPACE_PREFIX)) {
+                if (localName.equals(FX_ID_ATTRIBUTE)) {
+                    // Verify that ID is a valid identifier
+                    if (value.equals(NULL_KEYWORD)) {
+                        throw new LoadException("Invalid identifier.");
+                    }
+
+                    for (int i = 0, n = value.length(); i < n; i++) {
+                        if (!Character.isJavaIdentifierPart(value.charAt(i))) {
+                            throw new LoadException("Invalid identifier.");
+                        }
+                    }
+
+                    id = value;
+
+                    // If the value defines an ID property, set it
+                    if (id != null) {
+                        IDProperty idProperty = value.getClass().getAnnotation(IDProperty.class);
+
+                        if (idProperty != null) {
+                            Map<String, Object> properties = getProperties();
+                            properties.put(idProperty.value(), id);
+                        }
+                    }
+                } else if (localName.equals(FX_CONTROLLER_ATTRIBUTE)) {
+                    if (current.parent != null) {
+                        throw new LoadException(FX_NAMESPACE_PREFIX + ":" + FX_CONTROLLER_ATTRIBUTE
+                            + " can only be applied to root element.");
+                    }
+
+                    if (controller != null) {
+                        throw new LoadException("Controller value already specified.");
+                    }
+
+                    if (!staticLoad) {
+                        Class<?> type;
+                        try {
+                            type = classLoader.loadClass(value);
+                        } catch (ClassNotFoundException exception) {
+                            throw new LoadException(exception);
+                        }
+
+                        try {
+                            if (controllerFactory == null) {
+                                setController(type.newInstance());
+                            } else {
+                                setController(controllerFactory.call(type));
+                            }
+                        } catch (InstantiationException exception) {
+                            throw new LoadException(exception);
+                        } catch (IllegalAccessException exception) {
+                            throw new LoadException(exception);
+                        }
+                    }
+                } else {
+                    throw new LoadException("Invalid attribute.");
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        public abstract Object constructValue() throws IOException;
+    }
+
+    // Element representing a class instance
+    private class InstanceDeclarationElement extends ValueElement {
+        public Class<?> type;
+
+        public String constant = null;
+        public String factory = null;
+
+        public InstanceDeclarationElement(Class<?> type) throws LoadException {
+            this.type = type;
+        }
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix != null
+                && prefix.equals(FX_NAMESPACE_PREFIX)) {
+                if (localName.equals(FX_VALUE_ATTRIBUTE)) {
+                    this.value = value;
+                } else if (localName.equals(FX_CONSTANT_ATTRIBUTE)) {
+                    constant = value;
+                } else if (localName.equals(FX_FACTORY_ATTRIBUTE)) {
+                    factory = value;
+                } else {
+                    super.processAttribute(prefix, localName, value);
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        @Override
+        public Object constructValue() throws IOException {
+            Object value;
+            if (this.value != null) {
+                value = BeanAdapter.coerce(this.value, type);
+            } else if (constant != null) {
+                value = BeanAdapter.getConstantValue(type, constant);
+            } else if (factory != null) {
+                Method factoryMethod;
+                try {
+                    factoryMethod = type.getMethod(factory);
+                } catch (NoSuchMethodException exception) {
+                    throw new LoadException(exception);
+                }
+
+                try {
+                    value = factoryMethod.invoke(null);
+                } catch (IllegalAccessException exception) {
+                    throw new LoadException(exception);
+                } catch (InvocationTargetException exception) {
+                    throw new LoadException(exception);
+                }
+            } else {
+                value = (builderFactory == null) ? null : builderFactory.getBuilder(type);
+
+                if (value == null) {
+                    try {
+                        value = type.newInstance();
+                    } catch (InstantiationException exception) {
+                        throw new LoadException(exception);
+                    } catch (IllegalAccessException exception) {
+                        throw new LoadException(exception);
+                    }
+                }
+            }
+
+            return value;
+        }
+    }
+
+    // Element representing an unknown type
+    private class UnknownTypeElement extends ValueElement {
+        // Map type representing an unknown value
+        @DefaultProperty("items")
+        public class UnknownValueMap extends AbstractMap<String, Object> {
+            private ArrayList<?> items = new ArrayList<Object>();
+            private HashMap<String, Object> values = new HashMap<String, Object>();
+
+            @Override
+            public Object get(Object key) {
+                if (key == null) {
+                    throw new NullPointerException();
+                }
+
+                return (key.equals(getClass().getAnnotation(DefaultProperty.class).value())) ?
+                    items : values.get(key);
+            }
+
+            @Override
+            public Object put(String key, Object value) {
+                if (key == null) {
+                    throw new NullPointerException();
+                }
+
+                if (key.equals(getClass().getAnnotation(DefaultProperty.class).value())) {
+                    throw new IllegalArgumentException();
+                }
+
+                return values.put(key, value);
+            }
+
+            @Override
+            public Set<Entry<String, Object>> entrySet() {
+                return Collections.emptySet();
+            }
+        }
+
+        @Override
+        public void processEndElement() throws IOException {
+            // No-op
+        }
+
+        @Override
+        public Object constructValue() throws LoadException {
+            return new UnknownValueMap();
+        }
+    }
+
+    // Element representing an include
+    private class IncludeElement extends ValueElement {
+        public String source = null;
+        public ResourceBundle resources = FXMLLoader.this.resources;
+        public Charset charset = FXMLLoader.this.charset;
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix == null) {
+                if (localName.equals(INCLUDE_SOURCE_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    source = value;
+                } else if (localName.equals(INCLUDE_RESOURCES_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    resources = ResourceBundle.getBundle(value);
+                } else if (localName.equals(INCLUDE_CHARSET_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    charset = Charset.forName(value);
+                } else {
+                    super.processAttribute(prefix, localName, value);
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        @Override
+        public Object constructValue() throws IOException {
+            if (source == null) {
+                throw new LoadException(INCLUDE_SOURCE_ATTRIBUTE + " is required.");
+            }
+
+            URL location;
+            if (source.charAt(0) == '/') {
+                location = classLoader.getResource(source.substring(1));
+            } else {
+                if (FXMLLoader.this.location == null) {
+                    throw new LoadException("Base location is undefined.");
+                }
+
+                location = new URL(FXMLLoader.this.location, source);
+            }
+
+            FXMLLoader fxmlLoader = new FXMLLoader(location, resources,
+                builderFactory, controllerFactory, charset,
+                loaders);
+            fxmlLoader.setClassLoader(classLoader);
+            fxmlLoader.setStaticLoad(staticLoad);
+
+            Object value = fxmlLoader.load();
+
+            if (id != null) {
+                String id = this.id + CONTROLLER_SUFFIX;
+                Object controller = fxmlLoader.getController();
+
+                namespace.put(id, controller);
+
+                if (FXMLLoader.this.controller != null) {
+                    Field field = getControllerFields().get(id);
+
+                    if (field != null) {
+                        try {
+                            field.set(FXMLLoader.this.controller, controller);
+                        } catch (IllegalAccessException exception) {
+                            throw new LoadException(exception);
+                        }
+                    }
+                }
+            }
+
+            return value;
+        }
+    }
+
+    // Element representing a reference
+    private class ReferenceElement extends ValueElement {
+        public String source = null;
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix == null) {
+                if (localName.equals(REFERENCE_SOURCE_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    source = value;
+                } else {
+                    super.processAttribute(prefix, localName, value);
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        @Override
+        public Object constructValue() throws LoadException {
+            if (source == null) {
+                throw new LoadException(REFERENCE_SOURCE_ATTRIBUTE + " is required.");
+            }
+
+            KeyPath path = KeyPath.parse(source);
+            if (!Expression.isDefined(namespace, path)) {
+                throw new LoadException("Value \"" + source + "\" does not exist.");
+            }
+
+            return Expression.get(namespace, path);
+        }
+    }
+
+    // Element representing a copy
+    private class CopyElement extends ValueElement {
+        public String source = null;
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix == null) {
+                if (localName.equals(COPY_SOURCE_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    source = value;
+                } else {
+                    super.processAttribute(prefix, localName, value);
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        @Override
+        public Object constructValue() throws LoadException {
+            if (source == null) {
+                throw new LoadException(COPY_SOURCE_ATTRIBUTE + " is required.");
+            }
+
+            KeyPath path = KeyPath.parse(source);
+            if (!Expression.isDefined(namespace, path)) {
+                throw new LoadException("Value \"" + source + "\" does not exist.");
+            }
+
+            Object sourceValue = Expression.get(namespace, path);
+            Class<?> sourceValueType = sourceValue.getClass();
+
+            Constructor<?> constructor = null;
+            try {
+                constructor = sourceValueType.getConstructor(sourceValueType);
+            } catch (NoSuchMethodException exception) {
+                // No-op
+            }
+
+            Object value;
+            if (constructor != null) {
+                try {
+                    value = constructor.newInstance(sourceValue);
+                } catch (InstantiationException exception) {
+                    throw new LoadException(exception);
+                } catch (IllegalAccessException exception) {
+                    throw new LoadException(exception);
+                } catch (InvocationTargetException exception) {
+                    throw new LoadException(exception);
+                }
+            } else {
+                throw new LoadException("Can't copy value " + sourceValue + ".");
+            }
+
+            return value;
+        }
+    }
+
+    // Element representing a predefined root value
+    private class RootElement extends ValueElement {
+        public String type = null;
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix == null) {
+                if (localName.equals(ROOT_TYPE_ATTRIBUTE)) {
+                    if (loadListener != null) {
+                        loadListener.readInternalAttribute(localName, value);
+                    }
+
+                    type = value;
+                } else {
+                    super.processAttribute(prefix, localName, value);
+                }
+            } else {
+                super.processAttribute(prefix, localName, value);
+            }
+        }
+
+        @Override
+        public Object constructValue() throws LoadException {
+            if (type == null) {
+                throw new LoadException(ROOT_TYPE_ATTRIBUTE + " is required.");
+            }
+
+            Class<?> type = getType(this.type);
+
+            if (type == null) {
+                throw new LoadException(this.type + " is not a valid type.");
+            }
+
+            Object value;
+            if (root == null) {
+                value = (builderFactory == null) ? null : builderFactory.getBuilder(type);
+
+                if (value == null) {
+                    try {
+                        value = type.newInstance();
+                    } catch (InstantiationException exception) {
+                        throw new LoadException(exception);
+                    } catch (IllegalAccessException exception) {
+                        throw new LoadException(exception);
+                    }
+                }
+            } else {
+                if (!type.isAssignableFrom(root.getClass())) {
+                    throw new LoadException("Root is not an instance of "
+                        + type.getName() + ".");
+                }
+
+                value = root;
+            }
+
+            return value;
+        }
+    }
+
+    // Element representing a property
+    private class PropertyElement extends Element {
+        public final String name;
+        public final Class<?> sourceType;
+        public final boolean readOnly;
+
+        public PropertyElement(String name, Class<?> sourceType) throws LoadException {
+            if (parent == null) {
+                throw new LoadException("Invalid root element.");
+            }
+
+            if (parent.value == null) {
+                throw new LoadException("Parent element does not support property elements.");
+            }
+
+            this.name = name;
+            this.sourceType = sourceType;
+
+            if (sourceType == null) {
+                // The element represents an instance property
+                if (name.startsWith(EVENT_HANDLER_PREFIX)) {
+                    throw new LoadException("\"" + name + "\" is not a valid element name.");
+                }
+
+            Map<String, Object> parentProperties = parent.getProperties();
+
+            if (parent.isTyped()) {
+                readOnly = parent.getValueAdapter().isReadOnly(name);
+            } else {
+                // If the map already defines a value for the property, assume
+                // that it is read-only
+                readOnly = parentProperties.containsKey(name);
+            }
+
+            if (readOnly) {
+                Object value = parentProperties.get(name);
+                if (value == null) {
+                    throw new LoadException("Invalid property.");
+                }
+
+                updateValue(value);
+            }
+            } else {
+                // The element represents a static property
+                readOnly = false;
+            }
+        }
+
+        @Override
+        public boolean isCollection() {
+            return (readOnly) ? super.isCollection() : false;
+        }
+
+        @Override
+        public void add(Object element) throws LoadException {
+            // Coerce the element to the list item type
+            if (parent.isTyped()) {
+                Type listType = parent.getValueAdapter().getGenericType(name);
+                element = BeanAdapter.coerce(element, BeanAdapter.getListItemType(listType));
+            }
+
+            // Add the item to the list
+            super.add(element);
+        }
+
+        @Override
+        public void set(Object value) throws LoadException {
+            // Update the value
+            updateValue(value);
+
+            if (sourceType == null) {
+                // Apply value to parent element's properties
+                parent.getProperties().put(name, value);
+            } else {
+                if (parent.value instanceof Builder) {
+                    // Defer evaluation of the property
+                    parent.staticPropertyElements.add(this);
+                } else {
+                    // Apply the static property value
+                    BeanAdapter.put(parent.value, sourceType, name, value);
+                }
+            }
+        }
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (!readOnly) {
+                throw new LoadException("Attributes are not supported for writable property elements.");
+            }
+
+            super.processAttribute(prefix, localName, value);
+        }
+
+        @Override
+        public void processEndElement() throws IOException {
+            super.processEndElement();
+
+            if (readOnly) {
+                processInstancePropertyAttributes();
+                processEventHandlerAttributes();
+            }
+        }
+
+        @Override
+        public void processCharacters() throws IOException {
+            if (!readOnly) {
+                String text = xmlStreamReader.getText();
+                text = extraneousWhitespacePattern.matcher(text).replaceAll(" ");
+
+                set(text.trim());
+            } else {
+                super.processCharacters();
+            }
+        }
+    }
+
+    // Element representing an unknown static property
+    private class UnknownStaticPropertyElement extends Element {
+        public UnknownStaticPropertyElement() throws LoadException {
+            if (parent == null) {
+                throw new LoadException("Invalid root element.");
+            }
+
+            if (parent.value == null) {
+                throw new LoadException("Parent element does not support property elements.");
+            }
+        }
+
+        @Override
+        public boolean isCollection() {
+            return false;
+        }
+
+        @Override
+        public void set(Object value) {
+            updateValue(value);
+        }
+
+        @Override
+        public void processCharacters() throws IOException {
+            String text = xmlStreamReader.getText();
+            text = extraneousWhitespacePattern.matcher(text).replaceAll(" ");
+
+            updateValue(text.trim());
+        }
+    }
+
+    // Element representing a script block
+    private class ScriptElement extends Element {
+        public String source = null;
+        public Charset charset = FXMLLoader.this.charset;
+
+        @Override
+        public boolean isCollection() {
+            return false;
+        }
+
+        @Override
+        public void processStartElement() throws IOException {
+            super.processStartElement();
+
+            if (source != null && !staticLoad) {
+                int i = source.lastIndexOf(".");
+                if (i == -1) {
+                    throw new LoadException("Cannot determine type of script \""
+                        + source + "\".");
+                }
+
+                String extension = source.substring(i + 1);
+                ScriptEngine scriptEngine;
+                ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+                try {
+                    Thread.currentThread().setContextClassLoader(classLoader);
+                    ScriptEngineManager scriptEngineManager = getScriptEngineManager();
+                    scriptEngine = scriptEngineManager.getEngineByExtension(extension);
+                } finally {
+                    Thread.currentThread().setContextClassLoader(oldLoader);
+                }
+
+                if (scriptEngine == null) {
+                    throw new LoadException("Unable to locate scripting engine for"
+                        + " extension " + extension + ".");
+                }
+
+                scriptEngine.setBindings(scriptEngineManager.getBindings(), ScriptContext.ENGINE_SCOPE);
+
+                try {
+                    URL location;
+                    if (source.charAt(0) == '/') {
+                        location = classLoader.getResource(source.substring(1));
+                    } else {
+                        if (FXMLLoader.this.location == null) {
+                            throw new LoadException("Base location is undefined.");
+                        }
+
+                        location = new URL(FXMLLoader.this.location, source);
+                    }
+
+                    InputStreamReader scriptReader = null;
+                    try {
+                        scriptReader = new InputStreamReader(location.openStream(), charset);
+                        scriptEngine.eval(scriptReader);
+                    } catch(ScriptException exception) {
+                        exception.printStackTrace();
+                    } finally {
+                        if (scriptReader != null) {
+                            scriptReader.close();
+                        }
+                    }
+                } catch (IOException exception) {
+                    throw new LoadException(exception);
+                }
+            }
+        }
+
+        @Override
+        public void processEndElement() throws IOException {
+            super.processEndElement();
+
+            if (value != null && !staticLoad) {
+                // Evaluate the script
+                try {
+                    scriptEngine.eval((String)value);
+                } catch (ScriptException exception) {
+                    System.err.println(exception.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public void processCharacters() throws LoadException {
+            if (source != null) {
+                throw new LoadException("Script source already specified.");
+            }
+
+            if (scriptEngine == null && !staticLoad) {
+                throw new LoadException("Page language not specified.");
+            }
+
+            updateValue(xmlStreamReader.getText());
+        }
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws IOException {
+            if (prefix == null
+                && localName.equals(SCRIPT_SOURCE_ATTRIBUTE)) {
+                if (loadListener != null) {
+                    loadListener.readInternalAttribute(localName, value);
+                }
+
+                source = value;
+            } else if (localName.equals(SCRIPT_CHARSET_ATTRIBUTE)) {
+                if (loadListener != null) {
+                    loadListener.readInternalAttribute(localName, value);
+                }
+
+                charset = Charset.forName(value);
+            } else {
+                throw new LoadException(prefix == null ? localName : prefix + ":" + localName
+                    + " is not a valid attribute.");
+            }
+        }
+    }
+
+    // Element representing a define block
+    private class DefineElement extends Element {
+        @Override
+        public boolean isCollection() {
+            return true;
+        }
+
+        @Override
+        public void add(Object element) {
+            // No-op
+        }
+
+        @Override
+        public void processAttribute(String prefix, String localName, String value)
+            throws LoadException{
+            throw new LoadException("Element does not support attributes.");
+        }
+    }
+
+    // Class representing an attribute of an element
+    private static class Attribute {
+        public final String name;
+        public final Class<?> sourceType;
+        public final String value;
+
+        public Attribute(String name, Class<?> sourceType, String value) {
+            this.name = name;
+            this.sourceType = sourceType;
+            this.value = value;
+        }
+    }
+
+    // Event handler that delegates to a method defined by the controller object
+    private static class ControllerMethodEventHandler implements EventHandler<Event> {
+        public final Object controller;
+        public final Method method;
+        public final boolean typed;
+
+        public ControllerMethodEventHandler(Object controller, Method method) {
+            this.controller = controller;
+            this.method = method;
+            this.typed = (method.getParameterTypes().length == 1);
+        }
+
+        @Override
+        public void handle(Event event) {
+            try {
+                if (typed) {
+                    method.invoke(controller, event);
+                } else {
+                    method.invoke(controller);
+                }
+            } catch (InvocationTargetException exception) {
+                throw new RuntimeException(exception);
+            } catch (IllegalAccessException exception) {
+                throw new RuntimeException(exception);
+            }
+        }
+    }
+
+    // Event handler implemented in script code
+    private static class ScriptEventHandler implements EventHandler<Event> {
+        public final String script;
+        public final ScriptEngine scriptEngine;
+
+        public ScriptEventHandler(String script, ScriptEngine scriptEngine) {
+            this.script = script;
+            this.scriptEngine = scriptEngine;
+        }
+
+        @Override
+        public void handle(Event event) {
+            // Don't pollute the page namespace with values defined in the script
+            Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
+            Bindings localBindings = scriptEngine.createBindings();
+            localBindings.put(EVENT_KEY, event);
+            scriptEngine.setBindings(localBindings, ScriptContext.ENGINE_SCOPE);
+
+            // Execute the script
+            try {
+                scriptEngine.eval(script);
+            } catch (ScriptException exception){
+                throw new RuntimeException(exception);
+            }
+
+            // Restore the original bindings
+            scriptEngine.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE);
+        }
+    }
+
+    // Observable list change listener
+    private static class ObservableListChangeAdapter implements ListChangeListener<Object> {
+        public final ObservableList<Object> source;
+        public final EventHandler<ObservableListChangeEvent<?>> handler;
+
+        public ObservableListChangeAdapter(ObservableList<Object> source,
+            EventHandler<ObservableListChangeEvent<?>> handler) {
+            this.source = source;
+            this.handler = handler;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public void onChanged(Change<? extends Object> change) {
+            while (change.next()) {
+                EventType<ObservableListChangeEvent<?>> eventType;
+                List<Object> removed = (List<Object>)change.getRemoved();
+
+                if (change.wasPermutated()) {
+                    eventType = ObservableListChangeEvent.UPDATE;
+                    removed = null;
+                } else if (change.wasAdded() && change.wasRemoved()) {
+                    eventType = ObservableListChangeEvent.UPDATE;
+                } else if (change.wasAdded()) {
+                    eventType = ObservableListChangeEvent.ADD;
+                } else if (change.wasRemoved()) {
+                    eventType = ObservableListChangeEvent.REMOVE;
+                } else {
+                    throw new UnsupportedOperationException();
+                }
+
+                handler.handle(new ObservableListChangeEvent<Object>(source,
+                    eventType, change.getFrom(), change.getTo(),
+                    removed));
+            }
+        }
+    }
+
+    // Observable map change listener
+    private static class ObservableMapChangeAdapter implements MapChangeListener<Object, Object> {
+        public final ObservableMap<Object, Object> source;
+        public final EventHandler<ObservableMapChangeEvent<?, ?>> handler;
+
+        public ObservableMapChangeAdapter(ObservableMap<Object, Object> source,
+            EventHandler<ObservableMapChangeEvent<?, ?>> handler) {
+            this.source = source;
+            this.handler = handler;
+        }
+
+        @Override
+        public void onChanged(Change<? extends Object, ? extends Object> change) {
+            EventType<ObservableMapChangeEvent<?, ?>> eventType;
+            if (change.wasAdded() && change.wasRemoved()) {
+                eventType = ObservableMapChangeEvent.UPDATE;
+            } else if (change.wasAdded()) {
+                eventType = ObservableMapChangeEvent.ADD;
+            } else if (change.wasRemoved()) {
+                eventType = ObservableMapChangeEvent.REMOVE;
+            } else {
+                throw new UnsupportedOperationException();
+            }
+
+            handler.handle(new ObservableMapChangeEvent<Object, Object>(source,
+                eventType, change.getKey(), change.getValueRemoved()));
+        }
+    }
+
+    // Property model change listener
+    private static class PropertyChangeAdapter implements ChangeListener<Object> {
+        public final Object source;
+        public final EventHandler<PropertyChangeEvent<?>> handler;
+
+        public PropertyChangeAdapter(Object source, EventHandler<PropertyChangeEvent<?>> handler) {
+            if (source == null) {
+                throw new NullPointerException();
+            }
+
+            if (handler == null) {
+                throw new NullPointerException();
+            }
+
+            this.source = source;
+            this.handler = handler;
+        }
+
+        @Override
+        public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
+            handler.handle(new PropertyChangeEvent<Object>(source, oldValue));
+        }
+    }
+
+    protected URL location;
+    protected ResourceBundle resources;
+
+    private ObservableMap<String, Object> namespace = FXCollections.observableHashMap();
+
+    protected Object root = null;
+    protected Object controller = null;
+
+    private BuilderFactory builderFactory;
+    private Callback<Class<?>, Object> controllerFactory;
+    private Charset charset;
+
+    private LinkedList<FXMLLoader> loaders;
+
+    private ClassLoader classLoader = defaultClassLoader;
+    private boolean staticLoad = false;
+    private LoadListener loadListener = null;
+
+    private XMLStreamReader xmlStreamReader = null;
+    private Element current = null;
+
+    private ScriptEngine scriptEngine = null;
+
+    private boolean template = false;
+
+    private LinkedList<String> packages = new LinkedList<String>();
+    private HashMap<String, Class<?>> classes = new HashMap<String, Class<?>>();
+
+    private HashMap<String, Field> controllerFields = null;
+    private HashMap<String, Method> controllerMethods = null;
+
+    private ScriptEngineManager scriptEngineManager = null;
+
+    private static ClassLoader defaultClassLoader;
+
+    private static final Pattern extraneousWhitespacePattern = Pattern.compile("\\s+");
+
+    public static final String DEFAULT_CHARSET_NAME = "UTF-8";
+
+    public static final String LANGUAGE_PROCESSING_INSTRUCTION = "language";
+    public static final String IMPORT_PROCESSING_INSTRUCTION = "import";
+
+    public static final String FX_NAMESPACE_PREFIX = "fx";
+    public static final String FX_CONTROLLER_ATTRIBUTE = "controller";
+    public static final String FX_ID_ATTRIBUTE = "id";
+    public static final String FX_VALUE_ATTRIBUTE = "value";
+    public static final String FX_CONSTANT_ATTRIBUTE = "constant";
+    public static final String FX_FACTORY_ATTRIBUTE = "factory";
+
+    public static final String INCLUDE_TAG = "include";
+    public static final String INCLUDE_SOURCE_ATTRIBUTE = "source";
+    public static final String INCLUDE_RESOURCES_ATTRIBUTE = "resources";
+    public static final String INCLUDE_CHARSET_ATTRIBUTE = "charset";
+
+    public static final String SCRIPT_TAG = "script";
+    public static final String SCRIPT_SOURCE_ATTRIBUTE = "source";
+    public static final String SCRIPT_CHARSET_ATTRIBUTE = "charset";
+
+    public static final String DEFINE_TAG = "define";
+
+    public static final String REFERENCE_TAG = "reference";
+    public static final String REFERENCE_SOURCE_ATTRIBUTE = "source";
+
+    public static final String ROOT_TAG = "root";
+    public static final String ROOT_TYPE_ATTRIBUTE = "type";
+
+    public static final String COPY_TAG = "copy";
+    public static final String COPY_SOURCE_ATTRIBUTE = "source";
+
+    public static final String EVENT_HANDLER_PREFIX = "on";
+    public static final String EVENT_KEY = "event";
+    public static final String CHANGE_EVENT_HANDLER_SUFFIX = "Change";
+
+    public static final String NULL_KEYWORD = "null";
+
+    public static final String ESCAPE_PREFIX = "\\";
+    public static final String RELATIVE_PATH_PREFIX = "@";
+    public static final String RESOURCE_KEY_PREFIX = "%";
+    public static final String EXPRESSION_PREFIX = "$";
+    public static final String BINDING_EXPRESSION_PREFIX = "{";
+    public static final String BINDING_EXPRESSION_SUFFIX = "}";
+
+    public static final String BI_DIRECTIONAL_BINDING_PREFIX = "#{";
+    public static final String BI_DIRECTIONAL_BINDING_SUFFIX = "}";
+
+    public static final String ARRAY_COMPONENT_DELIMITER = ",";
+
+    public static final String LOCATION_KEY = "location";
+    public static final String RESOURCES_KEY = "resources";
+
+    public static final String CONTROLLER_METHOD_PREFIX = "#";
+    public static final String CONTROLLER_KEYWORD = "controller";
+    public static final String CONTROLLER_SUFFIX = "Controller";
+
+    public static final String INITIALIZE_METHOD_NAME = "initialize";
+
+    static {
+        defaultClassLoader = Thread.currentThread().getContextClassLoader();
+
+        if (defaultClassLoader == null) {
+            defaultClassLoader = ClassLoader.getSystemClassLoader();
+        }
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     */
+    public FXMLLoader() {
+        this((URL)null);
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     */
+    public FXMLLoader(URL location) {
+        this(location, null);
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     * @param resources
+     */
+    public FXMLLoader(URL location, ResourceBundle resources) {
+        this(location, resources, new JavaFXBuilderFactory());
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     */
+    public FXMLLoader(URL location, ResourceBundle resources, BuilderFactory builderFactory) {
+        this(location, resources, builderFactory, null);
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     * @param controllerFactory
+     */
+    public FXMLLoader(URL location, ResourceBundle resources, BuilderFactory builderFactory,
+        Callback<Class<?>, Object> controllerFactory) {
+        this(location, resources, builderFactory, controllerFactory, Charset.forName(DEFAULT_CHARSET_NAME));
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param charset
+     */
+    public FXMLLoader(Charset charset) {
+        this(null, null, null, null, charset);
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     * @param controllerFactory
+     * @param charset
+     */
+    public FXMLLoader(URL location, ResourceBundle resources, BuilderFactory builderFactory,
+        Callback<Class<?>, Object> controllerFactory, Charset charset) {
+        this(location, resources, builderFactory, controllerFactory, charset,
+            new LinkedList<FXMLLoader>());
+    }
+
+    /**
+     * Creates a new FXMLLoader instance.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     * @param controllerFactory
+     * @param charset
+     * @param loaders
+     */
+    public FXMLLoader(URL location, ResourceBundle resources, BuilderFactory builderFactory,
+        Callback<Class<?>, Object> controllerFactory, Charset charset,
+        LinkedList<FXMLLoader> loaders) {
+        setLocation(location);
+        setResources(resources);
+        setBuilderFactory(builderFactory);
+        setControllerFactory(controllerFactory);
+        setCharset(charset);
+
+        this.loaders = loaders;
+    }
+
+    /**
+     * Returns the location used to resolve relative path attribute values.
+     */
+    public URL getLocation() {
+        return location;
+    }
+
+    /**
+     * Sets the location used to resolve relative path attribute values.
+     *
+     * @param location
+     */
+    public void setLocation(URL location) {
+        this.location = location;
+    }
+
+    /**
+     * Returns the resources used to resolve resource key attribute values.
+     */
+    public ResourceBundle getResources() {
+        return resources;
+    }
+
+    /**
+     * Sets the resources used to resolve resource key attribute values.
+     *
+     * @param resources
+     */
+    public void setResources(ResourceBundle resources) {
+        this.resources = resources;
+    }
+
+    /**
+     * Returns the namespace used by this loader.
+     */
+    public ObservableMap<String, Object> getNamespace() {
+        return namespace;
+    }
+
+    /**
+     * Returns the root of the object hierarchy.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getRoot() {
+        return (T)root;
+    }
+
+    /**
+     * Sets the root of the object hierarchy. The value passed to this method
+     * is used as the value of the <tt>&lt;fx:root%gt;</tt> tag. This method
+     * must be called prior to loading the document when using
+     * <tt>&lt;fx:root%gt;</tt>.
+     *
+     * @param root
+     * The root of the object hierarchy.
+     */
+    public void setRoot(Object root) {
+        this.root = root;
+    }
+
+    /**
+     * Returns the controller associated with the root object.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getController() {
+        return (T)controller;
+    }
+
+    /**
+     * Sets the controller associated with the root object. The value passed to
+     * this method is used as the value of the <tt>fx:controller</tt> attribute.
+     * This method must be called prior to loading the document when using
+     * controller event handlers when an <tt>fx:controller</tt> attribute is not
+     * specified in the document.
+     *
+     * @param controller
+     * The controller to associate with the root object.
+     */
+    public void setController(Object controller) {
+        this.controller = controller;
+
+        if (controller == null) {
+            namespace.remove(CONTROLLER_KEYWORD);
+        } else {
+            namespace.put(CONTROLLER_KEYWORD, controller);
+        }
+
+        controllerFields = null;
+        controllerMethods = null;
+    }
+
+    /**
+     * Returns the template flag.
+     */
+    public boolean isTemplate() {
+        return template;
+    }
+
+    /**
+     * Sets the template flag. Setting this value to <tt>true</tt> can improve
+     * performance when using a single loader instance to reload the same FXML
+     * document multiple times. See the documentation for the {@link #load()}
+     * method for more information.
+     *
+     * @param template
+     * The template flag.
+     */
+    public void setTemplate(boolean template) {
+        this.template = template;
+    }
+
+    /**
+     * Returns the builder factory used by this loader.
+     */
+    public BuilderFactory getBuilderFactory() {
+        return builderFactory;
+    }
+
+    /**
+     * Sets the builder factory used by this loader.
+     *
+     * @param builderFactory
+     */
+    public void setBuilderFactory(BuilderFactory builderFactory) {
+        this.builderFactory = builderFactory;
+    }
+
+    /**
+     * Returns the controller factory used by this serializer.
+     */
+    public Callback<Class<?>, Object> getControllerFactory() {
+        return controllerFactory;
+    }
+
+    /**
+     * Sets the controller factory used by this serializer.
+     *
+     * @param controllerFactory
+     */
+    public void setControllerFactory(Callback<Class<?>, Object> controllerFactory) {
+        this.controllerFactory = controllerFactory;
+    }
+
+    /**
+     * Returns the character set used by this loader.
+     */
+    public Charset getCharset() {
+        return charset;
+    }
+
+    /**
+     * Sets the charset used by this loader.
+     *
+     * @param charset
+     */
+    public void setCharset(Charset charset) {
+        if (charset == null) {
+            throw new NullPointerException("charset is null.");
+        }
+
+        this.charset = charset;
+    }
+
+    /**
+     * Returns the classloader used by this serializer.
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    /**
+     * Sets the classloader used by this serializer and clears any existing
+     * imports (see {@link #setTemplate(boolean)}).
+     *
+     * @param classLoader
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+        if (classLoader == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.classLoader = classLoader;
+
+        clearImports();
+    }
+
+    /**
+     * Returns the static load flag.
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public boolean isStaticLoad() {
+        // SB-dependency: RT-21226 has been filed to track this
+        return staticLoad;
+    }
+
+    /**
+     * Sets the static load flag.
+     *
+     * @param staticLoad
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public void setStaticLoad(boolean staticLoad) {
+        // SB-dependency: RT-21226 has been filed to track this
+        this.staticLoad = staticLoad;
+    }
+
+    /**
+     * Returns this loader's load listener.
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public LoadListener getLoadListener() {
+        // SB-dependency: RT-21228 has been filed to track this
+        return loadListener;
+    }
+
+    /**
+     * Sets this loader's load listener.
+     *
+     * @param loadListener
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public void setLoadListener(LoadListener loadListener) {
+        // SB-dependency: RT-21228 has been filed to track this
+        this.loadListener = loadListener;
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document. The location from which
+     * the document will be loaded must have been set by a prior call to
+     * {@link #setLocation(URL)}.
+     * <p>
+     * When the "template" flag is set to <tt>false</tt> (the default), this
+     * method will clear the imports before loading the document's content.
+     * When "template" is <tt>true</tt>, the imports will not be cleared, and
+     * the root value will be set to <tt>null</tt> before the content is
+     * loaded. This helps improve performance on subsequent loads by
+     * eliminating the overhead of loading the classes referred to by the
+     * document.
+     *
+     * @return
+     * The loaded object hierarchy.
+     */
+    public Object load() throws IOException {
+        if (location == null) {
+            throw new IllegalStateException("Location is not set.");
+        }
+
+        InputStream inputStream = null;
+        Object value;
+        try {
+            inputStream = location.openStream();
+            value = load(inputStream);
+        } catch (IOException exception) {
+            logException(exception);
+            throw exception;
+        } catch (RuntimeException exception) {
+            logException(exception);
+            throw exception;
+        } finally {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param inputStream
+     * An input stream containing the FXML data to load.
+     *
+     * @return
+     * The loaded object hierarchy.
+     */
+    @SuppressWarnings("dep-ann")
+    public Object load(InputStream inputStream) throws IOException {
+        if (inputStream == null) {
+            throw new NullPointerException("inputStream is null.");
+        }
+
+        if (template) {
+            setRoot(null);
+        } else {
+            clearImports();
+        }
+
+        // Initialize the namespace
+        namespace.put(LOCATION_KEY, location);
+        namespace.put(RESOURCES_KEY, resources);
+
+        // Clear the script engine
+        scriptEngine = null;
+
+        // Create the parser
+        try {
+            XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
+            xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true);
+
+            // Some stream readers incorrectly report an empty string as the prefix
+            // for the default namespace; correct this as needed
+            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset);
+            xmlStreamReader = new StreamReaderDelegate(xmlInputFactory.createXMLStreamReader(inputStreamReader)) {
+                @Override
+                public String getPrefix() {
+                    String prefix = super.getPrefix();
+
+                    if (prefix != null
+                        && prefix.length() == 0) {
+                        prefix = null;
+                    }
+
+                    return prefix;
+                }
+
+                @Override
+                public String getAttributePrefix(int index) {
+                    String attributePrefix = super.getAttributePrefix(index);
+
+                    if (attributePrefix != null
+                        && attributePrefix.length() == 0) {
+                        attributePrefix = null;
+                    }
+
+                    return attributePrefix;
+                }
+            };
+        } catch (XMLStreamException exception) {
+            throw new LoadException(exception);
+        }
+
+        // Push this loader onto the stack
+        loaders.push(this);
+
+        // Parse the XML stream
+        try {
+            while (xmlStreamReader.hasNext()) {
+                int event = xmlStreamReader.next();
+
+                switch (event) {
+                    case XMLStreamConstants.PROCESSING_INSTRUCTION: {
+                        processProcessingInstruction();
+                        break;
+                    }
+
+                    case XMLStreamConstants.COMMENT: {
+                        processComment();
+                        break;
+                    }
+
+                    case XMLStreamConstants.START_ELEMENT: {
+                        processStartElement();
+                        break;
+                    }
+
+                    case XMLStreamConstants.END_ELEMENT: {
+                        processEndElement();
+                        break;
+                    }
+
+                    case XMLStreamConstants.CHARACTERS: {
+                        processCharacters();
+                        break;
+                    }
+                }
+            }
+        } catch (XMLStreamException exception) {
+            throw new LoadException(exception);
+        }
+
+        if (controller != null) {
+            if (controller instanceof Initializable) {
+                ((Initializable)controller).initialize(location, resources);
+            } else {
+                // Inject controller fields
+                HashMap<String, Field> controllerFields = getControllerFields();
+
+                Field locationField = controllerFields.get(LOCATION_KEY);
+                if (locationField != null) {
+                    try {
+                        locationField.set(controller, location);
+                    } catch (IllegalAccessException exception) {
+                        // TODO Throw when Initializable is deprecated/removed
+                        // throw new LoadException(exception);
+                    }
+                }
+
+                Field resourcesField = controllerFields.get(RESOURCES_KEY);
+                if (resourcesField != null) {
+                    try {
+                        resourcesField.set(controller, resources);
+                    } catch (IllegalAccessException exception) {
+                        // TODO Throw when Initializable is deprecated/removed
+                        // throw new LoadException(exception);
+                    }
+                }
+
+                // Initialize the controller
+                Method initializeMethod = getControllerMethods().get(INITIALIZE_METHOD_NAME);
+
+                if (initializeMethod != null) {
+                    try {
+                        initializeMethod.invoke(controller);
+                    } catch (IllegalAccessException exception) {
+                        // TODO Throw when Initializable is deprecated/removed
+                        // throw new LoadException(exception);
+                    } catch (InvocationTargetException exception) {
+                        throw new LoadException(exception);
+                    }
+                }
+            }
+        }
+
+        // Pop this loader off of the stack
+        loaders.pop();
+
+        // Clear the parser
+        xmlStreamReader = null;
+
+        return root;
+    }
+
+    private void clearImports() {
+        packages.clear();
+        classes.clear();
+    }
+
+    private void logException(Exception exception) {
+        String message = exception.getMessage();
+        if (message == null) {
+            message = exception.getClass().getName();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder(message);
+        messageBuilder.append("\n");
+
+        for (FXMLLoader loader : loaders) {
+            messageBuilder.append(loader.location.getPath());
+
+            if (loader.current != null) {
+                messageBuilder.append(":");
+                messageBuilder.append(loader.current.lineNumber);
+            }
+
+            messageBuilder.append("\n");
+        }
+
+        StackTraceElement[] stackTrace = exception.getStackTrace();
+        if (stackTrace != null) {
+            for (int i = 0; i < stackTrace.length; i++) {
+                messageBuilder.append("  at ");
+                messageBuilder.append(stackTrace[i].toString());
+                messageBuilder.append("\n");
+            }
+        }
+
+        System.err.println(messageBuilder.toString());
+    }
+
+    /**
+     * Returns the current line number.
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public int getLineNumber() {
+        return xmlStreamReader.getLocation().getLineNumber();
+    }
+
+    /**
+     * Returns the current parse trace.
+     *
+     * @treatAsPrivate
+     * @deprecated
+     */
+    public ParseTraceElement[] getParseTrace() {
+        ParseTraceElement[] parseTrace = new ParseTraceElement[loaders.size()];
+
+        int i = 0;
+        for (FXMLLoader loader : loaders) {
+            parseTrace[i++] = new ParseTraceElement(loader.location, (loader.current != null) ?
+                loader.current.lineNumber : -1);
+        }
+
+        return parseTrace;
+    }
+
+    private void processProcessingInstruction() throws LoadException {
+        String piTarget = xmlStreamReader.getPITarget().trim();
+
+        if (piTarget.equals(LANGUAGE_PROCESSING_INSTRUCTION)) {
+            processLanguage();
+        } else if (piTarget.equals(IMPORT_PROCESSING_INSTRUCTION)) {
+            processImport();
+        }
+    }
+
+    private void processLanguage() throws LoadException {
+        if (scriptEngine != null) {
+            throw new LoadException("Page language already set.");
+        }
+
+        String language = xmlStreamReader.getPIData();
+
+        if (loadListener != null) {
+            loadListener.readLanguageProcessingInstruction(language);
+        }
+
+        if (!staticLoad) {
+            ScriptEngineManager scriptEngineManager = getScriptEngineManager();
+            scriptEngine = scriptEngineManager.getEngineByName(language);
+            scriptEngine.setBindings(scriptEngineManager.getBindings(), ScriptContext.ENGINE_SCOPE);
+        }
+    }
+
+    private void processImport() throws LoadException {
+        String target = xmlStreamReader.getPIData().trim();
+
+        if (loadListener != null) {
+            loadListener.readImportProcessingInstruction(target);
+        }
+
+        if (target.endsWith(".*")) {
+            importPackage(target.substring(0, target.length() - 2));
+        } else {
+            importClass(target);
+        }
+    }
+
+    private void processComment() throws LoadException {
+        if (loadListener != null) {
+            loadListener.readComment(xmlStreamReader.getText());
+        }
+    }
+
+    private void processStartElement() throws IOException {
+        // Create the element
+        createElement();
+
+        // Process the start tag
+        current.processStartElement();
+
+        // Set the root value
+        if (root == null) {
+            root = current.value;
+        }
+    }
+
+    private void createElement() throws IOException {
+        String prefix = xmlStreamReader.getPrefix();
+        String localName = xmlStreamReader.getLocalName();
+
+        if (prefix == null) {
+            int i = localName.lastIndexOf('.');
+
+            if (Character.isLowerCase(localName.charAt(i + 1))) {
+                String name = localName.substring(i + 1);
+
+                if (i == -1) {
+                    // This is an instance property
+                    if (loadListener != null) {
+                        loadListener.beginPropertyElement(name, null);
+                    }
+
+                    current = new PropertyElement(name, null);
+                } else {
+                    // This is a static property
+                    Class<?> sourceType = getType(localName.substring(0, i));
+
+                    if (sourceType != null) {
+                        if (loadListener != null) {
+                            loadListener.beginPropertyElement(name, sourceType);
+                        }
+
+                        current = new PropertyElement(name, sourceType);
+                    } else if (staticLoad) {
+                        // The source type was not recognized
+                        if (loadListener != null) {
+                            loadListener.beginUnknownStaticPropertyElement(localName);
+                        }
+
+                        current = new UnknownStaticPropertyElement();
+                    } else {
+                        throw new LoadException(localName + " is not a valid property.");
+                    }
+                }
+            } else {
+                if (current == null && root != null) {
+                    throw new LoadException("Root value already specified.");
+                }
+
+                Class<?> type = getType(localName);
+
+                if (type != null) {
+                    if (loadListener != null) {
+                        loadListener.beginInstanceDeclarationElement(type);
+                    }
+
+                    current = new InstanceDeclarationElement(type);
+                } else if (staticLoad) {
+                    // The type was not recognized
+                    if (loadListener != null) {
+                        loadListener.beginUnknownTypeElement(localName);
+                    }
+
+                    current = new UnknownTypeElement();
+                } else {
+                    throw new LoadException(localName + " is not a valid type.");
+                }
+            }
+        } else if (prefix.equals(FX_NAMESPACE_PREFIX)) {
+            if (localName.equals(INCLUDE_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginIncludeElement();
+                }
+
+                current = new IncludeElement();
+            } else if (localName.equals(REFERENCE_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginReferenceElement();
+                }
+
+                current = new ReferenceElement();
+            } else if (localName.equals(COPY_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginCopyElement();
+                }
+
+                current = new CopyElement();
+            } else if (localName.equals(ROOT_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginRootElement();
+                }
+
+                current = new RootElement();
+            } else if (localName.equals(SCRIPT_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginScriptElement();
+                }
+
+                current = new ScriptElement();
+            } else if (localName.equals(DEFINE_TAG)) {
+                if (loadListener != null) {
+                    loadListener.beginDefineElement();
+                }
+
+                current = new DefineElement();
+            } else {
+                throw new LoadException(prefix + ":" + localName + " is not a valid element.");
+            }
+        } else {
+            throw new LoadException("Unexpected namespace prefix: " + prefix + ".");
+        }
+    }
+
+    private void processEndElement() throws IOException {
+        current.processEndElement();
+
+        if (loadListener != null) {
+            loadListener.endElement(current.value);
+        }
+
+        // Move up the stack
+        current = current.parent;
+    }
+
+    private void processCharacters() throws IOException {
+        // Process the characters
+        if (!xmlStreamReader.isWhiteSpace()) {
+            current.processCharacters();
+        }
+    }
+
+    private void importPackage(String name) throws LoadException {
+        packages.add(name);
+    }
+
+    private void importClass(String name) throws LoadException {
+        try {
+            loadType(name, true);
+        } catch (ClassNotFoundException exception) {
+            throw new LoadException(exception);
+        }
+    }
+
+    private Class<?> getType(String name) throws LoadException {
+        Class<?> type = null;
+
+        if (Character.isLowerCase(name.charAt(0))) {
+            // This is a fully-qualified class name
+            try {
+                type = loadType(name, false);
+            } catch (ClassNotFoundException exception) {
+                // No-op
+            }
+        } else {
+            // This is an unqualified class name
+            type = classes.get(name);
+
+            if (type == null) {
+                // The class has not been loaded yet; look it up
+                for (String packageName : packages) {
+                    try {
+                        type = loadTypeForPackage(packageName, name);
+                    } catch (ClassNotFoundException exception) {
+                        // No-op
+                    }
+
+                    if (type != null) {
+                        break;
+                    }
+                }
+
+                if (type != null) {
+                    classes.put(name, type);
+                }
+            }
+        }
+
+        return type;
+    }
+
+    private Class<?> loadType(String name, boolean cache) throws ClassNotFoundException {
+        int i = name.indexOf('.');
+        int n = name.length();
+        while (i != -1
+            && i < n
+            && Character.isLowerCase(name.charAt(i + 1))) {
+            i = name.indexOf('.', i + 1);
+        }
+
+        if (i == -1 || i == n) {
+            throw new ClassNotFoundException();
+        }
+
+        String packageName = name.substring(0, i);
+        String className = name.substring(i + 1);
+
+        Class<?> type = loadTypeForPackage(packageName, className);
+
+        if (cache) {
+            classes.put(className, type);
+        }
+
+        return type;
+    }
+
+    // TODO Rename to loadType() when deprecated static version is removed
+    private Class<?> loadTypeForPackage(String packageName, String className) throws ClassNotFoundException {
+        return classLoader.loadClass(packageName + "." + className.replace('.', '$'));
+    }
+
+    protected HashMap<String, Field> getControllerFields() throws LoadException {
+        if (controllerFields == null) {
+            controllerFields = new HashMap<String, Field>();
+
+            Class<?> controllerType = controller.getClass();
+            Class<?> type = controllerType;
+
+            while (type != Object.class) {
+                Field[] fields = type.getDeclaredFields();
+
+                for (int i = 0; i < fields.length; i++) {
+                    Field field = fields[i];
+                    int modifiers = field.getModifiers();
+
+                    // Only add fields that are visible to this controller type
+                    if (type == controllerType
+                        || (modifiers & Modifier.PRIVATE) == 0) {
+                        // Ensure that the field is accessible
+                        if ((modifiers & Modifier.PUBLIC) == 0
+                            && field.getAnnotation(FXML.class) != null) {
+                            try {
+                                field.setAccessible(true);
+                            } catch (SecurityException exception) {
+                                throw new LoadException(exception);
+                            }
+                        }
+
+                        controllerFields.put(field.getName(), field);
+                    }
+                }
+
+                type = type.getSuperclass();
+            }
+        }
+
+        return controllerFields;
+    }
+
+    protected HashMap<String, Method> getControllerMethods() throws LoadException {
+        if (controllerMethods == null) {
+            controllerMethods = new HashMap<String, Method>();
+
+            Class<?> controllerType = controller.getClass();
+            Class<?> type = controllerType;
+
+            while (type != Object.class) {
+                Method[] methods = type.getDeclaredMethods();
+
+                for (int i = 0; i < methods.length; i++) {
+                    Method method = methods[i];
+                    int modifiers = method.getModifiers();
+
+                    // Only add methods that are visible to this controller type
+                    if (type == controllerType
+                        || (modifiers & Modifier.PRIVATE) == 0) {
+                        // Ensure that the method is accessible
+                        if ((modifiers & Modifier.PUBLIC) == 0
+                            && method.getAnnotation(FXML.class) != null) {
+                            try {
+                                method.setAccessible(true);
+                            } catch (SecurityException exception) {
+                                throw new LoadException(exception);
+                            }
+                        }
+
+                        // Add this method to the map if:
+                        // a) it is the initialize() method, or
+                        // b) it takes a single event argument, or
+                        // c) it takes no arguments and a handler with this
+                        //    name has not already been defined
+                        String methodName = method.getName();
+                        Class<?>[] parameterTypes = method.getParameterTypes();
+
+                        if (methodName.equals(INITIALIZE_METHOD_NAME)) {
+                            if (parameterTypes.length == 0) {
+                                controllerMethods.put(method.getName(), method);
+                            }
+                        } else if ((parameterTypes.length == 1 && Event.class.isAssignableFrom(parameterTypes[0]))
+                            || (parameterTypes.length == 0 && !controllerMethods.containsKey(methodName))) {
+                            controllerMethods.put(method.getName(), method);
+                        }
+                    }
+                }
+
+                type = type.getSuperclass();
+            }
+        }
+
+        return controllerMethods;
+    }
+
+    private ScriptEngineManager getScriptEngineManager() {
+        if (scriptEngineManager == null) {
+            scriptEngineManager = new javax.script.ScriptEngineManager();
+            scriptEngineManager.setBindings(new SimpleBindings(namespace));
+        }
+
+        return scriptEngineManager;
+    }
+
+    /**
+     * Loads a type using the default class loader.
+     *
+     * @param packageName
+     * @param className
+     *
+     * @deprecated
+     * This method now delegates to {@link #getDefaultClassLoader()}.
+     */
+    public static Class<?> loadType(String packageName, String className) throws ClassNotFoundException {
+        return loadType(packageName + "." + className.replace('.', '$'));
+    }
+
+    /**
+     * Loads a type using the default class loader.
+     *
+     * @param className
+     *
+     * @deprecated
+     * This method now delegates to {@link #getDefaultClassLoader()}.
+     */
+    public static Class<?> loadType(String className) throws ClassNotFoundException {
+        return Class.forName(className, true, defaultClassLoader);
+    }
+
+    /**
+     * Returns the default class loader.
+     */
+    public static ClassLoader getDefaultClassLoader() {
+        return defaultClassLoader;
+    }
+
+    /**
+     * Sets the default class loader.
+     *
+     * @param defaultClassLoader
+     * The default class loader to use when loading classes.
+     */
+    public static void setDefaultClassLoader(ClassLoader defaultClassLoader) {
+        if (defaultClassLoader == null) {
+            throw new NullPointerException();
+        }
+
+        FXMLLoader.defaultClassLoader = defaultClassLoader;
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param location
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(URL location) throws IOException {
+        return (T)load(location, null);
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param location
+     * @param resources
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(URL location, ResourceBundle resources) throws IOException {
+        return (T)load(location, resources, new JavaFXBuilderFactory());
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(URL location, ResourceBundle resources, BuilderFactory builderFactory)
+        throws IOException {
+        return (T)load(location, resources, builderFactory, null);
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     * @param controllerFactory
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(URL location, ResourceBundle resources, BuilderFactory builderFactory,
+        Callback<Class<?>, Object> controllerFactory) throws IOException {
+        return (T)load(location, resources, builderFactory, controllerFactory, Charset.forName(DEFAULT_CHARSET_NAME));
+    }
+
+    /**
+     * Loads an object hierarchy from a FXML document.
+     *
+     * @param location
+     * @param resources
+     * @param builderFactory
+     * @param controllerFactory
+     * @param charset
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(URL location, ResourceBundle resources, BuilderFactory builderFactory,
+        Callback<Class<?>, Object> controllerFactory, Charset charset) throws IOException {
+        if (location == null) {
+            throw new NullPointerException("Location is required.");
+        }
+
+        FXMLLoader fxmlLoader = new FXMLLoader(location, resources, builderFactory, controllerFactory, charset);
+
+        return (T)fxmlLoader.load();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/Initializable.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+/**
+ * Controller initialization interface.
+ * <p>
+ * <em>NOTE</em> This interface has been superseded by automatic injection of
+ * <code>location</code> and <code>resources</code> properties into the
+ * controller. {@link FXMLLoader} will now automatically call any suitably
+ * annotated no-arg <code>initialize()</code> method defined by the controller.
+ * It is recommended that the injection approach be used whenever possible.
+ */
+public interface Initializable {
+    /**
+     * Called to initialize a controller after its root element has been
+     * completely processed.
+     *
+     * @param location
+     * The location used to resolve relative paths for the root object, or
+     * <tt>null</tt> if the location is not known.
+     *
+     * @param resources
+     * The resources used to localize the root object, or <tt>null</tt> if
+     * the root object was not localized.
+     */
+    public void initialize(URL location, ResourceBundle resources);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/JavaFXBuilderFactory.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.collections.ObservableMap;
+import javafx.scene.Node;
+import javafx.util.Builder;
+import javafx.util.BuilderFactory;
+
+import com.sun.javafx.fxml.BeanAdapter;
+import com.sun.javafx.fxml.builder.*;
+import java.util.Arrays;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.scene.text.Font;
+import javafx.scene.web.WebView;
+
+/**
+ * JavaFX builder factory.
+ */
+public final class JavaFXBuilderFactory implements BuilderFactory {
+    private final JavaFXBuilder NO_BUILDER = new JavaFXBuilder();
+
+    private final Map<Class<?>, JavaFXBuilder> builders = new HashMap<Class<?>, JavaFXBuilder>();
+
+    private final ClassLoader classLoader;
+    private final boolean alwaysUseBuilders;
+
+    /**
+     * Default constructor.
+     */
+    public JavaFXBuilderFactory() {
+        this(FXMLLoader.getDefaultClassLoader(), false);
+    }
+
+    /**
+     * @treatAsPrivate
+     * This constructor is for internal use only.
+     *
+     * @deprecated
+     */
+    public JavaFXBuilderFactory(boolean alwaysUseBuilders) {
+        // SB-dependency: RT-21230 has been filed to track this
+        this(FXMLLoader.getDefaultClassLoader(), alwaysUseBuilders);
+    }
+
+    /**
+     * Constructor that takes a class loader.
+     *
+     * @param classLoader
+     */
+    public JavaFXBuilderFactory(ClassLoader classLoader) {
+        this(classLoader, false);
+    }
+
+    /**
+     * @treatAsPrivate
+     * This constructor is for internal use only.
+     *
+     * @deprecated
+     */
+    public JavaFXBuilderFactory(ClassLoader classLoader, boolean alwaysUseBuilders) {
+        // SB-dependency: RT-21230 has been filed to track this
+        if (classLoader == null) {
+            throw new NullPointerException();
+        }
+
+        this.classLoader = classLoader;
+        this.alwaysUseBuilders = alwaysUseBuilders;
+    }
+
+    @Override
+    public Builder<?> getBuilder(Class<?> type) {
+        Builder<?> builder;
+
+        if (type == Scene.class) {
+            builder = new JavaFXSceneBuilder();
+        } else if (type == Font.class) {
+            builder = new JavaFXFontBuilder();
+        } else if (type == Image.class) {
+            builder = new JavaFXImageBuilder();
+        } else if (type == URL.class) {
+            builder = new URLBuilder(classLoader);
+        } else {
+            Builder<Object> objectBuilder = null;
+            JavaFXBuilder typeBuilder = builders.get(type);
+
+            if (typeBuilder != NO_BUILDER) {
+                if (typeBuilder == null) {
+                    // We want to retun a builder here
+                    // only for those classes that reqire it.
+                    // For now we assume that an object that has a default
+                    // constructor does not require a builder. This is the case
+                    // for most platform classes, except those handled above.
+                    // We may need to add other exceptions to the rule if the need
+                    // arises...
+                    //
+                    boolean hasDefaultConstructor;
+                    try {
+                        type.getConstructor();
+                        // found!
+                        // forces the factory  to return a builder if there is one.
+                        // TODO: delete the line below when we are sure that both
+                        //       builders and default constructors are working!
+                        if (alwaysUseBuilders) throw new Exception();
+
+                        hasDefaultConstructor = true;
+                    } catch (Exception x) {
+                        hasDefaultConstructor = false;
+                    }
+
+                    // Force the loader to use a builder for WebView even though
+                    // it defines a default constructor
+                    if (!hasDefaultConstructor || type == WebView.class) {
+                        try {
+                            typeBuilder = createTypeBuilder(type);
+                        } catch (ClassNotFoundException ex) {
+                            // no builder... will fail later when the FXMLLoader
+                            // will try to instantiate the bean...
+                        }
+                    }
+
+                    builders.put(type, typeBuilder == null ? NO_BUILDER : typeBuilder);
+                }
+                if (typeBuilder != null) {
+                    objectBuilder = typeBuilder.createBuilder();
+                }
+            }
+
+            builder = objectBuilder;
+        }
+
+        return builder;
+    }
+
+    JavaFXBuilder createTypeBuilder(Class<?> type) throws ClassNotFoundException {
+        JavaFXBuilder typeBuilder = null;
+        Class<?> builderClass = classLoader.loadClass(type.getName() + "Builder");
+        try {
+            typeBuilder = new JavaFXBuilder(builderClass);
+        } catch (Exception ex) {
+            //TODO should be reported
+            Logger.getLogger(JavaFXBuilderFactory.class.getName()).
+                    log(Level.WARNING, "Failed to instantiate JavaFXBuilder for " + builderClass, ex);
+        }
+        if (!alwaysUseBuilders) {
+            Logger.getLogger(JavaFXBuilderFactory.class.getName()).
+                    log(Level.FINER, "class {0} requires a builder.", type);
+        }
+        return typeBuilder;
+    }
+}
+
+/**
+ * JavaFX builder.
+ */
+final class JavaFXBuilder {
+    private static final Object[]   NO_ARGS = {};
+    private static final Class<?>[] NO_SIG = {};
+
+    private final Class<?>           builderClass;
+    private final Method             createMethod;
+    private final Method             buildMethod;
+    private final Map<String,Method> methods = new HashMap<String, Method>();
+    private final Map<String,Method> getters = new HashMap<String,Method>();
+    private final Map<String,Method> setters = new HashMap<String,Method>();
+
+    final class ObjectBuilder extends AbstractMap<String, Object> implements Builder<Object> {
+        private final Map<String,Object> containers = new HashMap<String,Object>();
+        private Object                   builder = null;
+        private Map<Object,Object>       properties;
+
+        private ObjectBuilder() {
+            try {
+                builder = createMethod.invoke(null);
+            } catch (Exception e) {
+                //TODO
+                throw new RuntimeException("Creation of the builder " + builderClass.getName() + " failed.", e);
+            }
+        }
+
+        @Override
+        public Object build() {
+            for (Iterator<Entry<String,Object>> iter = containers.entrySet().iterator(); iter.hasNext(); ) {
+                Entry<String, Object> entry = iter.next();
+
+                put(entry.getKey(), entry.getValue());
+            }
+
+            Object res;
+            try {
+                res = buildMethod.invoke(builder, NO_ARGS);
+                // TODO:
+                // temporary special case for Node properties until
+                // platform builders are fixed
+                if (properties != null && res instanceof Node) {
+                    ((Map<Object, Object>)((Node)res).getProperties()).putAll(properties);
+                }
+            } catch (InvocationTargetException exception) {
+                throw new RuntimeException(exception);
+            } catch (IllegalAccessException exception) {
+                throw new RuntimeException(exception);
+            } finally {
+                builder = null;
+            }
+
+            return res;
+        }
+
+        @Override
+        public int size() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isEmpty() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return (getTemporaryContainer(key.toString()) != null);
+        }
+
+        @Override
+        public boolean containsValue(Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object get(Object key) {
+            return getTemporaryContainer(key.toString());
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object put(String key, Object value) {
+            // TODO:
+            // temporary hack: builders don't have a method for properties...
+            if (Node.class.isAssignableFrom(getTargetClass()) && "properties".equals(key)) {
+                properties = (Map<Object,Object>) value;
+                return null;
+            }
+            try {
+                Method m = methods.get(key);
+                if (m == null) {
+                    m = findMethod(key);
+                    methods.put(key, m);
+                }
+                try {
+                    final Class<?> type = m.getParameterTypes()[0];
+
+                    // If the type is an Array, and our value is a list,
+                    // we simply convert the list into an array. Otherwise,
+                    // we treat the value as a string and split it into a
+                    // list using the array component delimiter.
+                    if (type.isArray()) {
+                        final List<?> list;
+                        if (value instanceof List) {
+                            list = (List<?>)value;
+                        } else {
+                            list = Arrays.asList(value.toString().split(FXMLLoader.ARRAY_COMPONENT_DELIMITER));
+                        }
+
+                        final Class<?> componentType = type.getComponentType();
+                        Object array = Array.newInstance(componentType, list.size());
+                        for (int i=0; i<list.size(); i++) {
+                            Array.set(array, i, BeanAdapter.coerce(list.get(i), componentType));
+                        }
+                        value = array;
+                    }
+
+                    m.invoke(builder, new Object[] {BeanAdapter.coerce(value, type)});
+                } catch (Exception e) {
+                    Logger.getLogger(JavaFXBuilder.class.getName()).log(Level.WARNING,
+                        "Method " + m.getName() + " failed", e);
+                }
+                //TODO Is it OK to return null here?
+                return null;
+            } catch (Exception e) {
+                //TODO Should be reported
+                Logger.getLogger(JavaFXBuilder.class.getName()).log(Level.WARNING,
+                        "Failed to set "+getTargetClass()+"."+key+" using "+builderClass, e);
+                return null;
+            }
+        }
+
+        // Should do this in BeanAdapter?
+        // This is used to support read-only collection property.
+        // This method must return a Collection of the appropriate type
+        // if 1. the property is read-only, and 2. the property is a collection.
+        // It must return null otherwise.
+        Object getReadOnlyProperty(String propName) {
+            if (setters.get(propName) != null) return null;
+            Method getter = getters.get(propName);
+            if (getter == null) {
+                Method setter = null;
+                Class<?> target = getTargetClass();
+                String suffix = Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
+                try {
+                    getter = target.getMethod("get"+ suffix, NO_SIG);
+                    setter = target.getMethod("set"+ suffix, getter.getReturnType());
+                } catch (Exception x) {
+                }
+                if (getter != null) {
+                    getters.put(propName, getter);
+                    setters.put(propName, setter);
+                }
+                if (setter != null) return null;
+            }
+
+            Class<?> type;
+            if (getter == null) {
+                // if we have found no getter it might be a constructor property
+                // try to get the type from the builder method.
+                final Method m = findMethod(propName);
+                if (m == null) {
+                    return null;
+                }
+                type = m.getParameterTypes()[0];
+                if (type.isArray()) type = List.class;
+            } else {
+                type = getter.getReturnType();
+            }
+
+            if (ObservableMap.class.isAssignableFrom(type)) {
+                return FXCollections.observableMap(new HashMap<Object, Object>());
+            } else if (Map.class.isAssignableFrom(type)) {
+                return new HashMap<Object, Object>();
+            } else if (ObservableList.class.isAssignableFrom(type)) {
+                return FXCollections.observableArrayList();
+            } else if (List.class.isAssignableFrom(type)) {
+                return new ArrayList<Object>();
+            } else if (Set.class.isAssignableFrom(type)) {
+                return new HashSet<Object>();
+            }
+            return null;
+        }
+
+        /**
+         * This is used to support read-only collection property.
+         * This method must return a Collection of the appropriate type
+         * if 1. the property is read-only, and 2. the property is a collection.
+         * It must return null otherwise.
+         **/
+        public Object getTemporaryContainer(String propName) {
+            Object o = containers.get(propName);
+            if (o == null) {
+                o = getReadOnlyProperty(propName);
+                if (o != null) {
+                    containers.put(propName, o);
+                }
+            }
+
+            return o;
+        }
+
+        @Override
+        public Object remove(Object key) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void putAll(Map<? extends String, ? extends Object> m) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Set<String> keySet() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Collection<Object> values() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Set<Entry<String, Object>> entrySet() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    JavaFXBuilder() {
+        builderClass = null;
+        createMethod = null;
+        buildMethod = null;
+    }
+
+    JavaFXBuilder(Class<?> builderClass) throws NoSuchMethodException, InstantiationException, IllegalAccessException {
+        this.builderClass = builderClass;
+        createMethod = builderClass.getMethod("create");
+        buildMethod = builderClass.getMethod("build");
+        assert Modifier.isStatic(createMethod.getModifiers());
+        assert !Modifier.isStatic(buildMethod.getModifiers());
+    }
+
+    Builder<Object> createBuilder() {
+        return new ObjectBuilder();
+    }
+
+    private Method findMethod(String name) {
+        if (name.length() > 1
+            && Character.isUpperCase(name.charAt(1))) {
+            name = Character.toUpperCase(name.charAt(0)) + name.substring(1);
+        }
+
+        for (Method m : builderClass.getMethods()) {
+            if (m.getName().equals(name)) {
+                return m;
+            }
+        }
+        throw new IllegalArgumentException("Method " + name + " could not be found at class " + builderClass.getName());
+    }
+
+    /**
+     * The type constructed by this builder.
+     * @return The type constructed by this builder.
+     */
+    public Class<?> getTargetClass() {
+        return buildMethod.getReturnType();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/LoadException.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.io.IOException;
+
+/**
+ * Thrown when an error is encountered during a load operation.
+ */
+public class LoadException extends IOException {
+    private static final long serialVersionUID = 0;
+
+    public LoadException() {
+        super();
+    }
+
+    public LoadException(String message) {
+        super(message);
+    }
+
+    public LoadException(Throwable cause) {
+        super(cause);
+    }
+
+    public LoadException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/ParseTraceElement.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.fxml;
+
+import java.net.URL;
+
+/**
+ * An element in a parse trace, as returned by
+ * {@link FXMLLoader#getParseTrace()}.
+ *
+ * @treatAsPrivate
+ */
+public class ParseTraceElement {
+    private URL location;
+    private int lineNumber;
+
+    public ParseTraceElement(URL location, int lineNumber) {
+        this.location = location;
+        this.lineNumber = lineNumber;
+    }
+
+    public URL getLocation() {
+        return location;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    @Override
+    public String toString() {
+        return ((location == null) ? "?" : location.getPath()) + ": " + lineNumber;
+    }
+}
Binary file javafx-fxml/src/javafx/fxml/doc-files/fx_code_header.jpg has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/doc-files/introduction_to_fxml.html	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,1017 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>Introduction to FXML | JavaFX 2.2</title>
+<meta name="description" content="The document introduces FXML, an XML-based declarative markup language for defining user interfaces in JavaFX 2.2 applications."/>
+<meta name="keywords" content="JavaFX 2.2, FXML, JavaFX GUI development, web development, Java application development, GUI applications, rich internet applications, RIA, expressive content"/>
+<style>
+body {
+    font-family: Helvetica, Arial, sans-serif;
+    font-size: 14px;
+}
+
+h1 {
+    font-size: 28px;
+    color:#385E76
+}
+
+h2 {
+    border-top: 3px dashed #DDD;
+    margin-top: 30px;
+    padding-top: 15px;
+    font-size: 22px;
+    color:#385E76
+}
+
+h3 {
+    background-color: #EEE;
+    padding: 3px;
+    font-size: 18px;
+}
+
+h4 {
+    font-size: 16px;
+    margin: 18px 0px 10px 0px;
+}
+
+h5 {
+    font-size: 14px;
+    font-style: italic;
+    margin: 18px 0px 10px 0px;
+}
+
+ul.contents li {
+    font-size: 12px;
+    font-weight:bold;
+    list-style-type:circle;
+    margin: 3px 0px 3px 0px;
+}
+
+ul.contents li li {
+    font-weight:normal;
+    list-style-type:circle;
+}
+
+a {
+    text-decoration:none;
+    color:#06C
+}
+
+p.subtitle {
+    font-style: italic;
+    color:#999;
+}
+
+span.code {
+    font-size: 10pt;
+    font-family: Courier New;
+}
+
+pre.code {
+    font-size: 10pt;
+    font-family: Courier New;
+    background: #dddddd;
+    padding:12;
+}
+
+span.variable {
+    font-style: italic;
+}
+
+span.filename {
+    font-size: 10pt;
+    font-family: Courier New;
+}
+
+div.caption {
+    font-style: italic;
+}
+
+p.copyright {
+    font-style: italic;
+}
+
+.fx-code-header {
+   background: #ffffff;
+   background-image:url(fx_code_header.jpg);
+   height: 50px;
+   width: 656px;
+}
+
+.version  {
+   font-family:Verdana, Arial, Helvetica, sans-serif;
+   font-size:10px;
+   padding-top:2px;
+   color:#265F7F;
+   line-height:1.2em;
+   position:relative;
+   left:425px;
+   top:10px;
+   width:200px;
+   text-align:right;
+}
+</style>
+</head>
+<body>
+<p>
+<div class="fx-code-header">
+<div class="version"><br/>Release: JavaFX 2.2</div>
+</div>
+</p>
+
+<h1>Introduction to FXML</h1>
+<p class="subtitle">Last updated: 6/21/2012</p>
+
+<h2>Contents</h2>
+<ul class="contents">
+<li><a href="#overview">Overview</a></li>
+<li>
+    <a href="#elements">Elements</a>
+    <ul>
+    <li>
+        <a href="#class_instance_elements">Class Instance Elements</a>
+        <ul>
+        <li><a href="#instance_declaration_elements">Instance Declarations</a></li>
+        <li><a href="#include_elements">&lt;fx:include&gt;</a></li>
+        <li><a href="#constant_elements">&lt;fx:constant&gt;</a></li>
+        <li><a href="#reference_elements">&lt;fx:reference&gt;</a></li>
+        <li><a href="#copy_elements">&lt;fx:copy&gt;</a></li>
+        <li><a href="#root_elements">&lt;fx:root&gt;</a></li>
+        </ul>
+    </li>
+
+    <li>
+        <a href="#property_elements">Property Elements</a>
+        <ul>
+        <li><a href="#property_setter_elements">Property Setters</a></li>
+        <li><a href="#read_only_list_property_elements">Read-Only List Properties</a></li>
+        <li><a href="#read_only_map_property_elements">Read-Only Map Properties</a></li>
+        <li><a href="#default_properties">Default Properties</a></li>
+        </ul>
+    </li>
+
+    <li><a href="#static_property_elements">Static Properties</a></li>
+    <li><a href="#define_elements">Define Blocks</a></li>
+    </ul>
+</li>
+<li>
+    <a href="#attributes">Attributes</a>
+    <ul>
+    <li>
+        <a href="#instance_property_attributes">Instance Properties</a>
+        <ul>
+        <li><a href="#location_resolution">Location Resolution</a></li>
+        <li><a href="#resource_resolution">Resource Resolution</a></li>
+        <li><a href="#variable_resolution">Variable Resolution</a></li>
+        <li><a href="#escape_sequences">Escape Sequences</a></li>
+        <li><a href="#expression_binding">Expression Binding</a></li>
+        </ul>
+    </li>
+
+    <li><a href="#static_property_attributes">Static Properties</a></li>
+    <li>
+        <a href="#event_handler_attributes">Event Handlers</a>
+        <ul>
+        <li><a href="#script_event_handlers">Script Event Handlers</a></li>
+        <li><a href="#controller_method_event_handlers">Controller Method Event Handlers</a></li>
+        </ul>
+    </li>
+    </ul>
+</li>
+<li><a href="#scripting">Scripting</a></li>
+<li>
+    <a href="#controllers">Controllers</a>
+    <ul>
+    <li><a href="#fxml_annotation">@FXML</a></li>
+    <li><a href="#nested_controllers">Nested Controllers</a></li>
+    </ul>
+</li>
+<li>
+    <a href="#fxmlloader">FXMLLoader</a>
+    <ul>
+    <li><a href="#custom_components">Custom Components</a></li>
+    </ul>
+</li>
+</ul>
+
+<h2><a name="overview">Overview</a></h2>
+<p>FXML is a scriptable, XML-based markup language for constructing Java object graphs. It provides a convenient alternative to constructing such graphs in procedural code, and is ideally suited to defining the user interface of a JavaFX application, since the hierarchical structure of an XML document closely parallels the structure of the JavaFX scene graph.</p>
+
+<p>This document introduces the FXML markup language and explains how it can be used to simplify development of JavaFX applications.</p>
+
+<h2><a name="elements">Elements</a></h2>
+<p>In FXML, an XML element represents one of the following:</p>
+<ul>
+<li>A class instance</li>
+<li>A property of a class instance</li>
+<li>A "static" property</li>
+<li>A "define" block</li>
+<li>A block of script code</li>
+</ul>
+
+<p>Class instances, instance properties, static properties, and define blocks are discussed in this section below. Scripting is discussed in a later section.</p>
+
+<h3><a name="class_instance_elements">Class Instance Elements</a></h3>
+<p>Class instances can be constructed in FXML in several ways. The most common is via instance declaration elements, which simply create a new instance of a class by name. Other ways of creating class instances include referencing existing values, copying existing values, and including external FXML files. Each is discussed in more detail below.</p>
+
+<h4><a name="instance_declaration_elements">Instance Declarations</a></h4>
+<p>If an element's tag name begins with an uppercase letter (and it is not a "static" property setter, described later), it is considered an instance declaration. When the FXML loader (also introduced later) encounters such an element, it creates an instance of that class.</p>
+
+<p>As in Java, class names can be fully-qualified (including the package name), or they can be imported using the "import" processing instruction (PI). For example, the following PI imports the <span class="code">javafx.scene.control.Label</span> class into the current FXML document’s namespace:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.Label?&gt;
+</pre>
+
+<p>This PI imports all classes from the javafx.scene.control package into the current namespace:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+</pre>
+
+<p>Any class that adheres to JavaBean constructor and property naming conventions can be readily instantiated and configured using FXML. The following is a simple but complete example that creates an instance of <span class="code">javafx.scene.control.Label</span> and sets its "text" property to "Hello, World!":</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.Label?&gt;
+&lt;Label text="Hello, World!"/&gt;
+</pre>
+
+<p>Note that the <span class="code">Label</span>’s "text" property in this example is set using an XML attribute. Properties can also be set using nested property elements. Property elements are discussed in more detail later in this section. Property attributes are discussed in a later section.</p>
+
+<p>Classes that don't conform to Bean conventions can also be constructed in FXML, using an object called a "builder". Builders are discussed in more detail later.</p>
+
+<h5>Maps</h5>
+<p>Internally, the FXML loader uses an instance of <span class="code">com.sun.javafx.fxml.BeanAdapter</span> to wrap an instantiated object and invoke its setter methods. This (currently) private class implements the <span class="code">java.util.Map</span> interface and allows a caller to get and set Bean property values as key/value pairs.</p>
+
+<p>If an element represents a type that already implements <span class="code">Map</span> (such as <span class="code">java.util.HashMap</span>), it is not wrapped and its <span class="code">get()</span> and <span class="code">put()</span> methods are invoked directly. For example, the following FXML creates an instance of <span class="code">HashMap</span> and sets its "foo" and "bar" values to "123" and "456", respectively:
+
+<pre class="code">
+&lt;HashMap foo="123" bar="456"/&gt;
+</pre>
+
+<h5>fx:value</h5>
+<p>The <span class="code">fx:value</span> attribute can be used to initialize an instance of a type that does not have a default constructor but provides a static <span class="code">valueOf(String)</span> method. For example, <span class="code">java.lang.String</span> as well as each of the primitive wrapper types define a <span class="code">valueOf()</span> method and can be constructed in FXML as follows:</p>
+
+<pre class="code">
+&lt;String fx:value="Hello, World!"/&gt;
+&lt;Double fx:value="1.0"/&gt;
+&lt;Boolean fx:value="false"/&gt;
+</pre>
+
+<p>Custom classes that define a static <span class="code">valueOf(String)</span> method can also be constructed this way.</p>
+
+<h5>fx:factory</h5>
+<p>The <span class="code">fx:factory</span> attribute is another means of creating objects whose classes do not have a default constructor. The value of the attribute is the name of a static, no-arg factory method for producing class instances. For example, the following markup creates an instance of an observable array list, populated with three string values:</p>
+
+<pre class="code">
+&lt;FXCollections fx:factory="observableArrayList"&gt;
+    &lt;String fx:value="A"/&gt;
+    &lt;String fx:value="B"/&gt;
+    &lt;String fx:value="C"/&gt;
+&lt;/FXCollections&gt;
+</pre>
+
+<h5>Builders</h5>
+<p>A third means of creating instances of classes that do not conform to Bean conventions (such as those representing immutable values) is a "builder". The builder design pattern delegates object construction to a mutable helper class (called a "builder") that is responsible for manufacturing instances of the immutable type.</p>
+
+<p>Builder support in FXML is provided by two interfaces. The <span class="code">javafx.util.Builder</span> interface defines a single method named <span class="code">build()</span> which is responsible for constructing the actual object:</p>
+
+<pre class="code">
+public interface Builder&lt;T&gt; {
+    public T build();
+}
+</pre>
+
+<p>A <span class="code">javafx.util.BuilderFactory</span> is responsible for producing builders that are capable of instantiating a given type:</p>
+
+<pre class="code">
+public interface BuilderFactory {
+    public Builder&lt;?&gt; getBuilder(Class&lt;?&gt; type);
+}
+</pre>
+
+<p>A default builder factory, <span class="code">JavaFXBuilderFactory</span>, is provided in the <span class="code">javafx.fxml</span> package. This factory is capable of creating and configuring most immutable JavaFX types. For example, the following markup uses the default builder to create an instance of the immutable <span class="code">javafx.scene.paint.Color</span> class:
+
+<pre class="code">
+&lt;Color red="1.0" green="0.0" blue="0.0"/&gt;
+</pre>
+
+<p>Note that, unlike Bean types, which are constructed when the element's start tag is processed, objects constructed by a builder are not instantiated until the element's closing tag is reached. This is because all of the required arguments may not be available until the element has been fully processed. For example, the Color object in the preceding example could also be written as:</p>
+
+<pre class="code">
+&lt;Color&gt;
+    &lt;red&gt;1.0&lt;/red&gt;
+    &lt;green&gt;0.0&lt;/green&gt;
+    &lt;blue&gt;0.0&lt;/blue&gt;
+&lt;/Color&gt;
+</pre>
+
+<p>The <span class="code">Color</span> instance cannot be fully constructed until all three of the color components are known.</p>
+
+<p>When processing markup for an object that will be constructed by a builder, the <span class="code">Builder</span> instances are treated like value objects - if a <span class="code">Builder</span> implements the <span class="code">Map</span> interface, the <span class="code">put()</span> method is used to set the builder's attribute values. Otherwise, the builder is wrapped in a <span class="code">BeanAdapter</span> and its properties are assumed to be exposed via standard Bean setters.</p>
+
+<h4><a name="include_elements">&lt;fx:include&gt;</a></h4>
+<p>The <span class="code">&lt;fx:include&gt;</span> tag creates an object from FXML markup defined in another file. It is used as follows:</p>
+
+<pre class="code">
+&lt;fx:include source="<span class="variable">filename</span>"/&gt;
+</pre>
+
+<p>where <span class="variable">filename</span> is the name of the FXML file to include. Values that begin with a leading slash character are treated as relative to the classpath. Values with no leading slash are considered relative to the path of the current document.</p>
+
+<p>For example, given the following markup:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+&lt;?import javafx.scene.layout.*?&gt;
+&lt;VBox xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;children&gt;
+        &lt;fx:include source="my_button.fxml"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<p>If <span class="filename">my_button.fxml</span> contains the following:
+
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+&lt;Button text="My Button"/&gt;
+</pre>
+
+<p>the resulting scene graph would contain a <span class="code">VBox</span> as a root object with a single <span class="code">Button</span> as a child node.</p>
+
+<p>Note the use of the "fx" namespace prefix. This is a reserved prefix that defines a number of elements and attributes that are used for internal processing of an FXML source file. It is generally declared on the root element of a FXML document. Other features provided by the "fx" namespace are described in the following sections.</p>
+
+<p><span class="code">&lt;fx:include&gt;</span> also supports attributes for specifying the name of the resource bundle that should be used to localize the included content, as well as the character set used to encode the source file. Resource resolution is discussed in a later section.</p>
+
+<h4><a name="constant_elements">&lt;fx:constant&gt;</a></h4>
+<p>The <span class="code">&lt;fx:constant&gt;</span> element creates a reference to a class constant. For example, the following markup sets the value of the "minWidth" property of a<span class="code">Button</span> instance to the value of the <span class="code">NEGATIVE_INFINITY</span> constant defined by the <span class="code">java.lang.Double</span> class:</p>
+
+<pre class="code">
+&lt;Button&gt;
+    &lt;minHeight&gt;&lt;Double fx:constant="NEGATIVE_INFINITY"/&gt;&lt;/minHeight&gt;
+&lt;/Button&gt;
+</pre>
+
+<h4><a name="reference_elements">&lt;fx:reference&gt;</a></h4>
+<p>The <span class="code">&lt;fx:reference&gt;</span> element creates a new reference to an existing element. Wherever this tag appears, it will effectively be replaced by the value of the named element. It is used in conjunction with either the <span class="code">fx:id</span> attribute or with a script variables, both of which are discussed in more detail in later sections. The "source" attribute of the <span class="code">&lt;fx:reference&gt;</span> element specifies the name of the object to which the new element will refer.</p>
+
+<p>For example, the following markup assigns a previously-defined <span class="code">Image</span> instance named "myImage" to the "image" property of an <span class="code">ImageView</span> control:</p>
+
+<pre class="code">
+&lt;ImageView&gt;
+    &lt;image&gt;
+        &lt;fx:reference source="myImage"/&gt;
+    &lt;/image&gt;
+&lt;/ImageView&gt;
+</pre>
+
+<p>Note that, since it is also possible to dereference a variable using the attribute variable resolution operator (discussed later in the <a href="#attributes">Attributes</a> section), <span class="code">fx:reference</span> is generally only used when a reference value must be specified as an element, such as when adding the reference to a collection:</p>
+
+<pre class="code">
+&lt;ArrayList&gt;
+    &lt;fx:reference source="element1"/&gt;
+    &lt;fx:reference source="element2"/&gt;
+    &lt;fx:reference source="element3"/&gt;
+&lt;/ArrayList&gt;
+</pre>
+
+<p>For most other cases, using an attribute is simpler and more concise.</p>
+
+<h4><a name="copy_elements">&lt;fx:copy&gt;</a></h4>
+<p>The <span class="code">&lt;fx:copy&gt;</span> element creates a copy of an existing element. Like <span class="code">&lt;fx:reference&gt;</span>, it is used with the fx:id attribute or a script variable. The element's "source" attribute specifies the name of the object that will be copied. The source type must define a copy constructor that will be used to construct the copy from the source value.</p>
+
+<p>At the moment, no JavaFX platform classes provide such a copy constructor, so this element is provided primarily for use by application developers. This may change in a future release.</p>
+
+<h4><a name="root_elements">&lt;fx:root&gt;</a></h4>
+<p>The <span class="code">&lt;fx:root&gt;</span> element creates a reference to a previously defined root element. It is only valid as the root node of an FXML document. <span class="code">&lt;fx:root&gt;</span> is used primarily when creating custom controls that are backed by FXML markup. This is discussed in more detail in the <a href="#fxmlloader">FXMLLoader</a> section.</p>
+
+<h3><a name="property_elements">Property Elements</a></h3>
+<p>Elements whose tag names begin with a lowercase letter represent object properties. A property element may represent one of the following:</p>
+
+<ul>
+<li>A property setter</li>
+<li>A read-only list property</li>
+<li>A read-only map property</li>
+</ul>
+
+<h4><a name="property_setter_elements">Property Setters</a></h4>
+<p>If an element represents a property setter, the contents of the element (which must be either a text node or a nested class instance element) are passed as the value to the setter for the property.</p>
+
+<p>For example, the following FXML creates an instance of the <span class="code">Label</span> class and sets the value of the label's "text" property to "Hello, World!":</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.Label?&gt;
+&lt;Label&gt;
+    &lt;text&gt;Hello, World!&lt;/text&gt;
+&lt;/Label&gt;
+</pre>
+
+<p>This produces the same result as the earlier example which used an attribute to set the "text" property:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.Label?&gt;
+&lt;Label text="Hello, World!"/&gt;
+</pre>
+
+<p>Property elements are generally used when the property value is a complex type that can't be represented using a simple string-based attribute value, or when the character length of the value is so long that specifying it as an attribute would have a negative impact on readability.</p>
+
+<h5>Type Coercion</h5>
+<p>FXML uses "type coercion" to convert property values to the appropriate type as needed. Type coercion is required because the only data types supported by XML are elements, text, and attributes (whose values are also text). However, Java supports a number of different data types including built-in primitive value types as well as extensible reference types.</p>
+
+<p>The FXML loader uses the <span class="code">coerce()</span> method of <span class="code">BeanAdapter</span> to perform any required type conversions. This method is capable of performing basic primitive type conversions such as <span class="code">String</span> to <span class="code">boolean</span> or <span class="code">int</span> to <span class="code">double</span>, and will also convert <span class="code">String</span> to <span class="code">Class</span> or <span class="code">String</span> to <span class="code">Enum</span>. Additional conversions can be implemented by defining a static <span class="code">valueOf()</span> method on the target type.</p>
+
+<h4><a name="read_only_list_property_elements">Read-Only List Properties</a></h4>
+<p>A read-only list property is a Bean property whose getter returns an instance of <span class="code">java.util.List</span> and has no corresponding setter method. The contents of a read-only list element are automatically added to the list as they are processed.</p>
+
+<p>For example, the "children" property of <span class="code">javafx.scene.Group</span> is a read-only list property representing the group's child nodes:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.*?&gt;
+&lt;?import javafx.scene.shape.*?&gt;
+&lt;Group xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;children&gt;
+        &lt;Rectangle fx:id="rectangle" x="10" y="10" width="320" height="240"
+            fill="#ff0000"/&gt;
+        ...
+    &lt;/children&gt;
+&lt;/Group&gt;
+</pre>
+
+<p>As each sub-element of the <span class="code">&lt;children&gt;</span> element is read, it is added to the list returned by <span class="code">Group#getChildren()</span>.
+
+<h4><a name="read_only_map_property_elements">Read-Only Map Properties</a></h4>
+<p>A read-only map property is a bean property whose getter returns an instance of <span class="code">java.util.Map</span> and has no corresponding setter method. The attributes of a read-only map element are applied to the map when the closing tag is processed.</p>
+
+<p>The "properties" property of <span class="code">javafx.scene.Node</span> is an example of a read-only map property. The following markup sets the "foo" and "bar" properties of a <span class="code">Label</span> instance to "123" and "456", respectively:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+&lt;Button&gt;
+    &lt;properties foo="123" bar="456"/&gt;
+&lt;/Button&gt;
+</pre>
+
+<p>Note that a read-only property whose type is neither a <span class="code">List</span> nor a <span class="code">Map</span> will be treated as if it were a read-only map. The return value of the getter method will be wrapped in a <span class="code">BeanAdapter</span> and can be used in the same way as any other read-only map.</p>
+
+<h4><a name="default_properties">Default Properties</a></h4>
+<p>A class may define a "default property" using the <span class="code">@DefaultProperty</span> annotation defined in the <span class="code">javafx.beans</span> package. If present, the sub-element representing the default property can be omitted from the markup.</p>
+
+<p>For example, since <span class="code">javafx.scene.layout.Pane</span> (the superclass of <span class="code">javafx.scene.layout.VBox</span>) defines a default property of "children", a <span class="code">&lt;children&gt;</span> element is not required; the loader will automatically add the sub-elements of the <span class="code">VBox</span> to the container's "children" collection:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.*?&gt;
+&lt;?import javafx.scene.shape.*?&gt;
+&lt;VBox xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;Button text="Click Me!"/&gt;
+    ...
+&lt;/VBox&gt;
+</pre>
+
+<p>Note that default properties are not limited to collections. If an element's default property refers to a scalar value, any sub-element of that element will be set as the value of the property.</p>
+
+<p>For example, since <span class="code">javafx.scene.control.ScrollPane</span> defines a default property of "content", a scroll pane containing a <span class="code">TextArea</span> as its content can be specified as follows:
+
+<pre class="code">
+&lt;ScrollPane&gt;
+    &lt;TextArea text="Once upon a time..."/&gt;
+&lt;/ScrollPane&gt;
+</pre>
+
+<p>Taking advantage of default properties can significantly reduce the verbosity of FXML markup.</p>
+
+<h3><a name="static_property_elements">Static Properties</a></h3>
+<p>An element may also represent a "static" property (sometimes called an "attached property"). Static properties are properties that only make sense in a particular context. They are not intrinsic to the class to which they are applied, but are defined by another class (often, the parent container of a control).</p>
+
+<p>Static properties are prefixed with the name of class that defines them. For example, The following FXML invokes the static setter for <span class="code">GridPane</span>'s "rowIndex" and "columnIndex" properties:</p>
+
+<pre class="code">
+&lt;GridPane&gt;
+    &lt;children&gt;
+        &lt;Label text="My Label"&gt;
+            &lt;GridPane.rowIndex&gt;0&lt;/GridPane.rowIndex&gt;
+       &lt;GridPane.columnIndex&gt;0&lt;/GridPane.columnIndex&gt;
+        &lt;/Label&gt;
+    &lt;/children&gt;
+&lt;/TabPane&gt;
+</pre>
+
+<p>This translates roughly to the following in Java:</p>
+
+<pre class="code">
+GridPane gridPane = new GridPane();
+
+Label label = new Label();
+label.setText("My Label");
+
+GridPane.setRowIndex(label, 0);
+GridPane.setColumnIndex(label, 0);
+
+gridPane.getChildren().add(label);
+</pre>
+
+The calls to <span class="code">GridPane#setRowIndex()</span> and <span class="code">GridPane#setColumnIndex()</span> "attach" the index data to the <span class="code">Label</span> instance. <span class="code">GridPane</span> then uses these during layout to arrange its children appropriately. Other containers, including <span class="code">AnchorPane</span>, <span class="code">BorderPane</span>, and <span class="code">StackPane</span>, define similar properties.</p>
+
+<p>As with instance properties, static property elements are generally used when the property value cannot be efficiently represented by an attribute value. Otherwise, static property attributes (discussed in a later section) will generally produce more concise and readable markup.</p>
+
+<h3><a name="define_elements">Define Blocks</a></h3>
+<p>The <span class="code">&lt;fx:define&gt;</span> element is used to create objects that exist outside of the object hierarchy but may need to be referred to elsewhere.</p>
+
+<p>For example, when working with radio buttons, it is common to define a <span class="code">ToggleGroup</span> that will manage the buttons' selection state. This group is not part of the scene graph itself, so should not be added to the buttons' parent. A define block can be used to create the button group without interfering with the overall structure of the document:</p>
+
+<pre class="code">
+&lt;VBox&gt;
+    &lt;fx:define&gt;
+        &lt;ToggleGroup fx:id="myToggleGroup"/&gt;
+    &lt;/fx:define&gt;
+    &lt;children&gt;
+        &lt;RadioButton text="A" toggleGroup="$myToggleGroup"/&gt;
+        &lt;RadioButton text="B" toggleGroup="$myToggleGroup"/&gt;
+        &lt;RadioButton text="C" toggleGroup="$myToggleGroup"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<p>Elements in define blocks are usually assigned an ID that can be used to refer to the element's value later. IDs are discussed in more detail in later sections.</p>
+
+<h2><a name="attributes">Attributes</a></h2>
+<p>An attribute in FXML may represent one of the following:</p>
+<ul>
+<li>A property of a class instance</li>
+<li>A "static" property</li>
+<li>An event handler</li>
+</ul>
+
+<p>Each are discussed in more detail in the following sections.</p>
+
+<h3><a name="instance_property_attributes">Instance Properties</a></h3>
+<p>Like property elements, attributes can also be used to configure the properties of a class instance. For example, the following markup creates a <span class="code">Button</span> whose text reads "Click Me!":</p>
+
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+&lt;Button text="Click Me!"/&gt;
+</pre>
+
+<p>As with property elements, property attributes support type coercion. When the following markup is processed, the "x", "y", "width", and "height" values will be converted to doubles, and the "fill" value will be converted to a <span class="code">Color</span>:</p>
+
+<pre class="code">
+&lt;Rectangle fx:id="rectangle" x="10" y="10" width="320" height="240"
+    fill="#ff0000"/&gt;
+</pre>
+
+<p>Unlike property elements, which are applied as they are processed, property attributes are not applied until the closing tag of their respective element is reached. This is done primarily to facilitate the case where an attribute value depends on some information that won't be available until after the element's content has been completely processed (for example, the selected index of a <span class="code">TabPane</span> control, which can't be set until all of the tabs have been added).</p>
+
+<p>Another key difference between property attributes and property elements in FXML is that attributes support a number of "resolution operators" that extend their functionality. The following operators are supported and are discussed in more detail below:</p>
+
+<ul>
+<li>Location resolution</li>
+<li>Resource resolution</li>
+<li>Variable resolution</li>
+</ul>
+
+<h4><a name="location_resolution">Location Resolution</a></h4>
+<p>As strings, XML attributes cannot natively represent typed location information such as a URL. However, it is often necessary to specify such locations in markup; for example, the source of an image resource. The location resolution operator (represented by an "@" prefix to the attribute value) is used to specify that an attribute value should be treated as a location relative to the current file rather than a simple string.</p>
+
+<p>For example, the following markup creates an ImageView and populates it with image data from <span class="filename">my_image.png</span>, which is assumed to be located at a path relative to the current FXML file:</p>
+
+<pre class="code">
+&lt;ImageView&gt;
+    &lt;image&gt;
+        &lt;Image url="@my_image.png"/&gt;
+    &lt;/image&gt;
+&lt;/ImageView&gt;
+</pre>
+
+<p>Since <span class="code">Image</span> is an immutable object, a builder is required to construct it. Alternatively, if <span class="code">Image</span> were to define a <span class="code">valueOf(URL)</span> factory method, the image view could be populated as follows:</p>
+
+<pre class="code">
+&lt;ImageView image="@my_image.png"/&gt;
+</pre>
+
+<p>The value of the "image" attribute would be converted to a URL by the FXML loader, then coerced to an <span class="code">Image</span> using the <span class="code">valueOf()</span> method.</p>
+
+<p>Note that whitespace values in the URL must be encoded; for example, to refer to a file named "My Image.png", the FXML document should contain the following:</p>
+
+<pre class="code">
+&lt;Image url="@My%20Image.png"/&gt;
+</pre>
+
+<p>rather than:</p>
+
+<pre class="code">
+&lt;Image url="@My Image.png"/&gt;
+</pre>
+
+<h4><a name="resource_resolution">Resource Resolution</a></h4>
+<p>In FXML, resource substitution can be performed at load time for localization purposes. When provided with an instance of <span class="code">java.util.ResourceBundle</span>, the FXML loader will replace instances of resource names with their locale-specific values. Resource names are identified by a "%" prefix, as shown below:</p>
+
+<pre class="code">
+&lt;Label text="%myText"/&gt;
+</pre>
+
+<p>If the loader is given a resource bundle defined as follows:</p>
+
+<pre class="code">
+myText = This is the text!
+</pre>
+
+<p>the output of the FXML loader would be a <span class="code">Label</span> instance containing the text "This is the text!".</p>
+
+<h4><a name="variable_resolution">Variable Resolution</a></h4>
+<p>An FXML document defines a variable namespace in which named elements and script variables may be uniquely identified. The variable resolution operator allows a caller to replace an attribute value with an instance of a named object before the corresponding setter method is invoked. Variable references are identified by a "$" prefix, as shown below:</p>
+
+<pre class="code">
+&lt;fx:define&gt;
+    &lt;ToggleGroup fx:id="myToggleGroup"/&gt;
+&lt;/fx:define&gt;
+...
+&lt;RadioButton text="A" toggleGroup="$myToggleGroup"/&gt;
+&lt;RadioButton text="B" toggleGroup="$myToggleGroup"/&gt;
+&lt;RadioButton text="C" toggleGroup="$myToggleGroup"/&gt;
+</pre>
+
+<p>Assigning an <span class="code">fx:id</span> value to an element creates a variable in the document's namespace that can later be referred to by variable dereference attributes, such as the "toggleGroup" attribute shown above, or in script code, discussed in a later section. Additionally, if the object's type defines an "id" property, this value will also be passed to the objects <span class="code">setId()</span> method.</p>
+
+<h4><a name="escape_sequences">Escape Sequences</a></h4>
+
+<p>If the value of an attribute begins with one of the resource resolution prefixes, the character can be escaped by prepending it with a leading backslash ("\") character. For example, the following markup creates a <span class="code">Label</span> instance whose text reads "$10.00":</p>
+
+<pre class="code">
+&lt;Label text="\$10.00"/&gt;
+</pre>
+
+<h4><a name="expression_binding">Expression Binding</a></h4>
+<p>Attribute variables as shown above are resolved once at load time. Later updates to the variables value are not automatically reflected in any properties to which the value was assigned. In many cases, this is sufficient; however, it is often convenient to "bind" a property value to a variable or expression such that changes to the variable are automatically propagated to the target property. Expression bindings can be used for this purpose.</p>
+
+<p>An expression binding also begins with the variable resolution operator, but is followed by a set of curly braces which wrap the expression value. For example, the following markup binds the value of a text input's "text" property to the "text" property of a <span class="code">Label</span> instance:</p>
+
+<pre class="code">
+&lt;TextField fx:id="textField"/&gt;
+&lt;Label text="${textField.text}"/&gt;
+</pre>
+
+<p>As the user types in the text input, the label's text content will be automatically updated.</p>
+
+<p>Only simple expressions that resolve to property values or page variables are currently supported. Support for more complex expressions involving boolean or other operators may be added in the future.</p>
+
+<h3><a name="static_property_attributes">Static Properties</a></h3>
+<p>Attributes representing static properties are handled similarly to static property elements and use a similar syntax. For example, the earlier <span class="code">GridPane</span> markup shown earlier to demonstrate static property elements could be rewritten as follows:</p>
+
+<pre class="code">
+&lt;GridPane&gt;
+    &lt;children&gt;
+        &lt;Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/&gt;
+    &lt;/children&gt;
+&lt;/TabPane&gt;
+</pre>
+
+<p>In addition to being more concise, static property attributes, like instance property attributes, support location, resource, and variable resolution operators, the only limitation being that it is not possible to create an expression binding to a static property.</p>
+
+<h3><a name="event_handler_attributes">Event Handlers</a></h3>
+<p>Event handler attributes are a convenient means of attaching behaviors to document elements. Any class that defines a <span class="code">setOn<span class="variable">Event</span>()</span> method can be assigned an event handler in markup, as can any observable property (via an "on<span class="variable">Property</span>Change" attribute).</p>
+
+<p>FXML supports two types of event handler attributes: script event handlers and controller method event handlers. Each are discussed below.</p>
+
+<h4><a name="script_event_handlers">Script Event Handlers</a></h4>
+<p>A script event handler is an event handler that executes script code when the event is fired, similar to event handlers in HTML. For example, the following script-based handler for the button's "onAction" event uses JavaScript to write the text "You clicked me!" to the console when the user presses the button:</p>
+
+<pre class="code">
+&lt;?language javascript?&gt;
+...
+
+&lt;VBox&gt;
+    &lt;children&gt;
+        &lt;Button text="Click Me!"
+            onAction="java.lang.System.out.println('You clicked me!');"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<p>Note the use of the language processing instruction at the beginning of the code snippet. This PI tells the FXML loader which scripting language should be used to execute the event handler. A page language must be specified whenever inline script is used in an FXML document, and can only be specified once per document. However, this does not apply to external scripts, which may be implemented using any number of supported scripting languages. Scripting is discussed in more detail in the next section.</p>
+
+<h4><a name="controller_method_event_handlers">Controller Method Event Handlers</a></h4>
+<p>A controller method event handler is a method defined by a document's "controller". A controller is an object that is associated with the deserialized contents of an FXML document and is responsible for coordinating the behaviors of the objects (often user interface elements) defined by the document.</p>
+
+<p>A controller method event handler is specified by a leading hash symbol followed by the name of the handler method. For example:</p>
+
+<pre class="code">
+&lt;VBox fx:controller="com.foo.MyController"
+    xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;children&gt;
+        &lt;Button text="Click Me!" onAction="#handleButtonAction"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<p>Note the use of the <span class="code">fx:controller</span> attribute on the root element. This attribute is used to associate a controller class with the document. If <span class="code">MyController</span> is defined as follows:</p>
+
+<pre class="code">
+package com.foo;
+
+public class MyController {
+    public void handleButtonAction(ActionEvent event) {
+        System.out.println("You clicked me!");
+    }
+}
+</pre>
+
+<p>the <span class="code">handleButtonAction()</span> will be called when the user presses the button, and the text "You clicked me!" will be written to the console.</p>
+
+<p>In general, a handler method should conform to the signature of a standard event handler; that is, it should take a single argument of a type that extends <span class="code">javafx.event.Event</span> and should return void (similar to an event delegate in C#). The event argument often carries important and useful information about the nature of the event; however, it is optional and may be omitted if desired.</p>
+
+<p>Controllers are discussed in more detail in a later section.</p>
+
+<h2><a name="scripting">Scripting</a></h2>
+The <span class="code">&lt;fx:script&gt;</span> tag allows a caller to import scripting code into or embed script within a FXML file. Any JVM scripting language can be used, including JavaScript, Groovy, and Clojure, among others. Script code is often used to define event handlers directly in markup or in an associated source file, since event handlers can often be written more concisely in more loosely-typed scripting languages than they can in a statically-typed language such as Java.</p>
+
+<p>For example, the following markup defines a function called <span class="code">handleButtonAction()</span> that is called by the action handler attached to the <span class="code">Button</span> element:</p>
+
+<pre class="code">
+&lt;?language javascript?&gt;
+
+&lt;?import javafx.scene.control.*?&gt;
+&lt;?import javafx.scene.layout.*?&gt;
+
+&lt;VBox xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;fx:script&gt;
+    importClass(java.lang.System);
+
+    function handleButtonAction(event) {
+       System.out.println('You clicked me!');
+    }
+    &lt;/fx:script&gt;
+
+    &lt;children&gt;
+        &lt;Button text="Click Me!" onAction="handleButtonAction(event);"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<p>Clicking the button triggers the event handler, which invokes the function, producing output identical to the previous examples.</p>
+
+<p>Script code may also be defined in external files. The previous example could be split into an FXML file and a JavaScript source file with no difference in functionality:</p>
+
+<div class="caption">example.fxml</div>
+<pre class="code">
+&lt;?import javafx.scene.control.*?&gt;
+&lt;?import javafx.scene.layout.*?&gt;
+
+&lt;VBox xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;fx:script source="example.js"/&gt;
+
+    &lt;children&gt;
+        &lt;Button text="Click Me!" onAction="handleButtonAction(event);"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<div class="caption">example.js</div>
+<pre class="code">
+importClass(java.lang.System);
+
+function handleButtonAction(event) {
+   System.out.println('You clicked me!');
+}
+</pre>
+
+<p>It is often preferable to separate code from markup in this way, since many text editors support syntax highlighting for the various scripting languages supported by the JVM. It can also help improve readability of the source code and markup.</p>
+
+<p>Note that script blocks are not limited to defining event handler functions. Script code is executed as it is processed, so it can also be used to dynamically configure the structure of the resulting output. As a simple example, the following FXML includes a script block that defines a variable named "labelText". The value of this variable is used to populate the text property of a <span class="code">Label</span> instance:</p>
+
+<pre class="code">
+&lt;fx:script&gt;
+var myText = "This is the text of my label.";
+&lt;/fx:script&gt;
+
+...
+
+&lt;Label text="$myText"/&gt;
+</pre>
+
+<h2><a name="controllers">Controllers</a></h2>
+<p>While it can be convenient to write simple event handlers in script, either inline or defined in external files, it is often preferable to define more complex application logic in a compiled, strongly-typed language such as Java. As discussed earlier, the <span class="code">fx:controller</span> attribute allows a caller to associate a "controller" class with an FXML document. A controller is a compiled class that implements the "code behind" the object hierarchy defined by the document.</p>
+
+<p>As shown earlier, controllers are often used to implement event handlers for user interface elements defined in markup:</p>
+
+<pre class="code">
+&lt;VBox fx:controller="com.foo.MyController"
+    xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;children&gt;
+        &lt;Button text="Click Me!" onAction="#handleButtonAction"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<pre class="code">
+package com.foo;
+
+public class MyController {
+    public void handleButtonAction(ActionEvent event) {
+        System.out.println("You clicked me!");
+    }
+}
+</pre>
+
+<p>In many cases, it is sufficient to simply declare event handlers in this manner. However, when more control over the behavior of the controller and the elements it manages is required, the controller can define an <span class="code">initialize()</span> method, which will be called once on an implementing controller when the contents of its associated document have been completely loaded:</p>
+
+<pre class="code">
+public void initialize();
+</pre>
+
+<p>This allows the implementing class to perform any necessary post-processing on the content. It also provides the controller with access to the resources that were used to load the document and the location that was used to resolve relative paths within the document (commonly equivalent to the location of the document itself).</p>
+
+<p>For example, the following code defines an <span class="code">initialize()</span> method that attaches an action handler to a button in code rather than via an event handler attribute, as was done in the previous example. The button instance variable is injected by the loader as the document is read. The resulting application behavior is identical:</p>
+
+<pre class="code">
+&lt;VBox fx:controller="com.foo.MyController"
+    xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;children&gt;
+        &lt;Button fx:id="button" text="Click Me!"/&gt;
+    &lt;/children&gt;
+&lt;/VBox&gt;
+</pre>
+
+<pre class="code">
+package com.foo;
+
+public class MyController implements Initializable {
+    public Button button;
+
+    @Override
+    public void initialize(URL location, Resources resources)
+        button.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
+            @Override
+            public void handle(ActionEvent event) {
+                System.out.println("You clicked me!");
+            }
+        });
+    }
+}
+</pre>
+
+<h3><a name="fxml_annotation">@FXML</a></h3>
+<p>Note that, in the previous examples, the controller member fields and event handler methods were declared as public so they can be set or invoked by the loader. In practice, this is not often an issue, since a controller is generally only visible to the FXML loader that creates it. However, for developers who prefer more restricted visibility for controller fields or handler methods, the <span class="code">javafx.fxml.FXML</span> annotation can be used. This annotation marks a protected or private class member as accessible to FXML.</span>
+
+<p>For example, the controllers from the previous examples could be rewritten as follows:</p>
+
+<pre class="code">
+package com.foo;
+
+public class MyController {
+    @FXML
+    private void handleButtonAction(ActionEvent event) {
+        System.out.println("You clicked me!");
+    }
+}
+</pre>
+
+<pre class="code">
+package com.foo;
+
+public class MyController implements Initializable {
+    @FXML private Button button;
+
+    @FXML
+    protected void initialize()
+        button.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
+            @Override
+            public void handle(ActionEvent event) {
+                System.out.println("You clicked me!");
+            }
+        });
+    }
+}
+</pre>
+
+<p>In the first version, the <span class="code">handleButtonAction()</span> is tagged with <span class="code">@FXML</span> to allow markup defined in the controller's document to invoke it. In the second example, the button field is annotated to allow the loader to set its value. The <span class="code">initialize()</span> method is similarly annotated.</p>
+
+<p>Note that the <span class="code">@FXML</span> annotation can currently only be used with trusted code. Because the FXML loader relies on reflection to set member fields and invoke member methods, it must call <span class="code">setAccessible()</span> on any non-public <span class="code">Field</span>. <span class="code">setAccessible()</span> is a privileged operation that can only be executed in a secure context. This may change in a future release.</p>
+
+<h3><a name="nested_controllers">Nested Controllers</a></h3>
+<p>Controller instances for nested FXML documents loaded via the <span class="code">&lt;fx:include&gt;</span> element are mapped directly to member fields of the including controller. This allows a developer to easily access functionality defined by an include (such as a dialog window presented by an application's main window controller). For example, given the following code:
+
+<div class="caption">main_window_content.fxml</div>
+<pre class="code">
+&lt;VBox fx:controller="com.foo.MainController"&gt;
+   &lt;fx:include fx:id="dialog" source="dialog.fxml"/&gt;
+   ...
+&lt;/VBox&gt;
+</pre>
+
+<div class="caption">MainController.java</div>
+<pre class="code">
+public class MainController extends Controller {
+    @FXML private Window dialog;
+    @FXML private DialogController dialogController;
+
+    ...
+}
+</pre>
+
+<p>when the controller's <span class="code">initialize()</span> method is called, the <span class="code">dialog</span> field will contain the root element loaded from the "dialog.fxml" include, and the <span class="code">dialogController</span> field will contain the include's controller. The main controller can then invoke methods on the included controller, to populate and show the dialog, for example.</p>
+
+<h2><a name="fxmlloader">FXMLLoader</a></h2>
+<p>The <span class="code">FXMLLoader</span> class is responsible for actually loading an FXML source file and returning the resulting object graph. For example, the following code loads an FXML file from a location on the classpath relative to the loading class and localizes it with a resource bundle named "com.foo.example". The type of the root element is assumed to be a subclass of <span class="code">javafx.scene.layout.Pane</span>, and the document is assumed to define a controller of type <span class="code">MyController</span>:</p>
+
+<pre class="code">
+URL location = getClass().getResource("example.fxml");
+ResourceBundle resources = ResourceBundle.getBundle("com.foo.example");
+FXMLLoader fxmlLoader = new FXMLLoader(location, resources);
+
+Pane root = (Pane)fxmlLoader.load();
+MyController controller = (MyController)fxmlLoader.getController();
+</pre>
+
+<p>Note that the output of an <span class="code">FXMLLoader#load()</span> operation is an instance hierarchy that reflects the actual named classes in the document, not <span class="code">org.w3c.dom</span> nodes representing those classes. Internally, <span class="code">FXMLLoader</span> uses the <span class="code">javax.xml.stream</span> API (also known as the <i>Streaming API for XML</i>, or <i>StAX</i>) to load an FXML document. StAX is an extremely efficient event-based XML parsing API that is conceptually similar to its W3C predecessor, SAX. It allows an FXML document to be processed in a single pass, rather than loaded into an intermediate DOM structure and then post-processed.</p>
+
+<h3><a name="custom_components">Custom Components</a></h3>
+<p>The <span class="code">setRoot()</span> and <span class="code">setController()</span> methods of <span class="code">FXMLLoader</span> allow a caller to inject document root and controller values, respectively, into the document namespace, rather than delegating creation of these values to <span class="code">FXMLLoader</span> itself. This allows a developer to easily create reusable controls that are internally implemented using markup, but (from an API perspective) appear identically to controls implemented programmatically.</p>
+
+<p>For example, the following markup defines the structure of a simple custom control containing a <span class="code">TextField</span> and a <span class="code">Button</span> instance. The root container is defined as an instance of <span class="code">javafx.scene.layout.VBox</span>:</p>
+
+<pre class="code">
+&lt;?import javafx.scene.*?&gt;
+&lt;?import javafx.scene.control.*?&gt;
+&lt;?import javafx.scene.layout.*?&gt;
+
+&lt;fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml"&gt;
+    &lt;TextField fx:id="textField"/&gt;
+    &lt;Button text="Click Me" onAction="#doSomething"/&gt;
+&lt;/fx:root&gt;
+</pre>
+
+<p>As mentioned earlier, the <span class="code">&lt;fx:root&gt;</span> tag creates a reference to a previously defined root element. The value of this element is obtained by calling the <span class="code">getRoot()</span> method of <span class="code">FXMLLoader</span>. Prior to calling <span class="code">load()</span>, the caller must specify this value via a call to <span class="code">setRoot()</span>. The caller may similarly provide a value for the document's controller by calling <span class="code">setController()</span>, which sets the value that will be used as the document's controller when the document is read. These two methods are commonly used together when creating custom FXML-based components.</p>
+
+<p>In the following example, the <span class="code">CustomControl</span> class extends <span class="code">VBox</span> (the type declared by the <span class="code">&lt;fx:root&gt;</span> element), and sets itself as both the root and controller of the FXML document in its constructor. When the document is loaded, the contents of <span class="code">CustomControl</span> will be populated with the contents of the previous FXML document:</p>
+
+<pre class="code">
+package fxml;
+
+import java.io.IOException;
+
+import javafx.beans.property.StringProperty;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.VBox;
+
+public class CustomControl extends VBox {
+    @FXML private TextField textField;
+
+    public CustomControl() {
+        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("custom_control.fxml"));
+        fxmlLoader.setRoot(this);
+        fxmlLoader.setController(this);
+
+        try {
+            fxmlLoader.load();
+        } catch (IOException exception) {
+            throw new RuntimeException(exception);
+        }
+    }
+
+    public String getText() {
+        return textProperty().get();
+    }
+
+    public void setText(String value) {
+        textProperty().set(value);
+    }
+
+    public StringProperty textProperty() {
+        return textField.textProperty();
+    }
+
+    @FXML
+    protected void doSomething() {
+        System.out.println("The button was clicked!");
+    }
+}
+</pre>
+
+<p>Now, callers can use instances of this control in code or in markup, just like any other control; e.g.:</p>
+
+<div class="caption">Java</div>
+<pre class="code">
+HBox hbox = new HBox();
+CustomControl customControl = new CustomControl();
+customControl.setText("Hello World!");
+hbox.getChildren().add(customControl);
+</pre>
+
+<div class="caption">FXML</div>
+<pre class="code">
+&lt;HBox&gt;
+    &lt;CustomControl text="Hello World!"/&gt;
+&lt;/HBox&gt;
+</pre>
+
+<hr>
+<p>
+<small>Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. Use is subject to <a href="http://docs.oracle.com/javafx/2/api/license.html">license terms</a>.
+</small>
+</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/src/javafx/fxml/package.html	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+<title>javafx.fxml</title>
+</head>
+<body>
+<p>Contains classes for loading an object hierarchy from markup. For more
+information, see the <a href="doc-files/introduction_to_fxml.html">Introduction
+to FXML</a> document.</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-fxml/test/com/oracle/javafx/fxml/test/CompareLoadPerformance.java	Tue Sep 25 13:06:21 2012 -0700
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.javafx.fxml.test;
+
+import java.io.IOException;
+
+import javafx.application.Application;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.HPos;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.Separator;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TextField;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.ColumnConstraints;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.RowConstraints;
+import javafx.scene.layout.TilePane;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+
+public class CompareLoadPerformance extends Application {
+    private static final Image ICON_48 = new Image(CompareLoadPerformance.class.getResourceAsStream("icon-48x48.png"));
+    private static final boolean USE_FXML = true;
+
+    @Override
+    public void start(Stage primaryStage) throws Exception {
+        TabPane tabPane = new TabPane();
+
+        Tab borderPaneTab = new Tab("BorderPane");
+        borderPaneTab.setContent(USE_FXML ? createBorderPaneFXML() : createBorderPane());
+        tabPane.getTabs().add(borderPaneTab);
+
+        Tab flowPaneTab = new Tab("FlowPane");
+        flowPaneTab.setContent(USE_FXML ? createFlowPaneFXML() : createFlowPane());
+        tabPane.getTabs().add(flowPaneTab);
+
+        Tab gridPaneTab = new Tab("GridPane");
+        gridPaneTab.setContent(USE_FXML ? createGridPaneFXML() : createGridPane());
+        tabPane.getTabs().add(gridPaneTab);
+
+        Tab tilePaneTab = new Tab("TilePane");
+        tilePaneTab.setContent(USE_FXML ? createTilePaneFXML() : createTilePane());
+        tabPane.getTabs().add(tilePaneTab);
+
+        primaryStage.setScene(new Scene(tabPane));
+        primaryStage.show();
+    }
+
+    private Node createBorderPane() {
+        long t0 = System.currentTimeMillis();
+
+        BorderPane borderPane = new BorderPane();
+
+        //Top content
+        Rectangle topRectangle = new Rectangle(400, 23, Color.DARKSEAGREEN);
+        topRectangle.setStroke(Color.BLACK);
+        borderPane.setTop(topRectangle);
+
+        //Left content
+        Label label1 = new Label("Left hand");
+        Label label2 = new Label("Choice One");
+        Label label3 = new Label("Choice Two");
+        Label label4 = new Label("Choice Three");
+        VBox leftVbox = new VBox();
+        leftVbox.getChildren().addAll(label1, label2, label3, label4);
+        borderPane.setLeft(leftVbox);
+
+        //Right content
+        Label rightlabel1 = new Label("Right hand");
+        Label rightlabel2 = new Label("Thing A");
+        Label rightlabel3 = new Label("Thing B");
+        VBox rightVbox = new VBox();
+        rightVbox.getChildren().addAll(rightlabel1, rightlabel2, rightlabel3);
+        borderPane.setRight(rightVbox);
+
+        //Center content
+        Label centerLabel = new Label("We're in the center area.");
+        ImageView imageView = new ImageView(ICON_48);
+
+        //Using AnchorPane only to position items in the center
+        AnchorPane centerAP = new AnchorPane();
+        AnchorPane.setTopAnchor(centerLabel, Double.valueOf(5));
+        AnchorPane.setLeftAnchor(centerLabel, Double.valueOf(20));
+        AnchorPane.setTopAnchor(imageView, Double.valueOf(40));
+        AnchorPane.setLeftAnchor(imageView, Double.valueOf(30));
+        centerAP.getChildren().addAll(centerLabel, imageView);
+        borderPane.setCenter(centerAP);
+
+        //Bottom content
+        Label bottomLabel = new Label("I am a status message, and I am at the bottom.");
+        borderPane.setBottom(bottomLabel);
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println("BorderPane " + (t1 - t0) + "ms");
+
+        return borderPane;
+    }
+
+    private Node createBorderPaneFXML() {
+        long t0 = System.currentTimeMillis();
+
+        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("border_pane.fxml"));
+        fxmlLoader.getNamespace().put("ICON_48", ICON_48);
+
+        BorderPane borderPane;
+        try {
+            borderPane = (BorderPane)fxmlLoader.load();
+        } catch (IOException exception) {
+            throw new RuntimeException(exception);
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println("BorderPane FXML " + (t1 - t0) + "ms");
+
+        return borderPane;
+    }
+
+    private Node createFlowPane() {
+        long t0 = System.currentTimeMillis();
+
+        final int ITEMS = 5;
+        FlowPane flowPane = new FlowPane(2, 4);
+        flowPane.setPrefWrapLength(200); //preferred wraplength
+        Label[] shortLabels = new Label[ITEMS];
+        Label[] longLabels = new Label[ITEMS];
+        ImageView[] imageViews = new ImageView[ITEMS];
+
+        for (int i = 0; i < ITEMS; i++) {
+            shortLabels[i] = new Label("Short label.");
+            longLabels[i] = new Label("I am a slightly longer label.");
+            imageViews[i] = new ImageView(ICON_48);
+            flowPane.getChildren().addAll(shortLabels[i], longLabels[i], imageViews[i]);
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println("FlowPane " + (t1 - t0) + "ms");
+
+        return flowPane;
+    }
+
+    private Node createFlowPaneFXML() {
+        long t0 = System.currentTimeMillis();
+
+        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("flow_pane.fxml"));
+        fxmlLoader.getNamespace().put("ICON_48", ICON_48);
+
+        FlowPane flowPane;
+        try {
+            flowPane = (FlowPane)fxmlLoader.load();
+        } catch (IOException exception) {
+            throw new RuntimeException(exception);
+        }
+
+        long t1 = System.currentTimeMillis();
+
+        System.out.println("FlowPane FXML " + (t1 - t0) + "ms");
+
+        return flowPane;
+    }
+
+    private Node createGridPane() {
+        long t0 = System.currentTimeMillis();
+
+        VBox vbox = new VBox();
+
+        //grid1 places the child by specifying the rows and columns in
+        // GridPane.setContraints()
+        Label grid1Caption = new Label("The example below shows GridPane content placement by specifying rows and columns:");
+        grid1Caption.setWrapText(true);
+        GridPane grid1 = new GridPane();
+        grid1.setHgap(4);
+        grid1.setVgap(6);
+        grid1.setPadding(new Insets(18, 18, 18, 18));
+        ObservableList<Node> content = grid1.getChildren();
+
+        Label label = new Label("Name:");
+        GridPane.setConstraints(label, 0, 0);
+        GridPane.setHalignment(label, HPos.RIGHT);
+        content.add(label);
+
+        label = new Label("John Q. Public");
+        GridPane.setConstraints(label, 1, 0, 2, 1);
+        GridPane.setHalignment(label, HPos.LEFT);
+        content.add(label);
+
+        label = new Label("Address:");
+        GridPane.setConstraints(label, 0, 1);
+        GridPane.setHalignment(label, HPos.RIGHT);
+        content.add(label);
+
+        label = new Label("12345 Main Street, Some City, CA");
+        GridPane.setConstraints(label, 1, 1, 5, 1);
+        GridPane.setHalignment(label, HPos.LEFT);
+        content.add(label);
+
+        vbox.getChildren().addAll(grid1Caption, grid1, new Separator());
+
+        //grid2 places the child by influencing the rows and columns themselves
+        //via GridRowInfo and GridColumnInfo. This grid uses the preferred
+        //width/height and max/min width/height.
+        Label grid2Caption = new Label("The example below shows GridPane content placement by influencing the rows and columns themselves.");
+        grid2Caption.setWrapText(true);
+        grid2Caption.setWrapText(true);
+        GridPane grid2 = new GridPane();
+        grid2.setPadding(new Insets(18, 18, 18, 18));
+        RowConstraints rowinfo = new RowConstraints(40, 40, 40);
+        ColumnConstraints colinfo = new ColumnConstraints(90, 90, 90);
+
+        for (int i = 0; i <= 2; i++) {
+            grid2.getRowConstraints().add(rowinfo);
+        }
+
+        for (int j = 0; j <= 2; j++) {
+            grid2.getColumnConstraints().add(colinfo);
+        }
+
+        Label category = new Label("Category:");
+        GridPane.setHalignment(category, HPos.RIGHT);
+        Label categoryValue = new Label("Wines");
+        Label company = new Label("Company:");
+        GridPane.setHalignment(company, HPos.RIGHT);
+        Label companyValue = new Label("Acme Winery");
+        Label rating = new Label("Rating:");
+        GridPane.setHalignment(rating, HPos.RIGHT);
+        Label ratingValue = new Label("Excellent");
+
+        ImageView imageView = new ImageView(ICON_48);
+        GridPane.setHalignment(imageView, HPos.CENTER);
+
+        //Place content
+        GridPane.setConstraints(category, 0, 0);
+        GridPane.setConstraints(categoryValue, 1, 0);
+        GridPane.setConstraints(company, 0, 1);
+        GridPane.setConstraints(companyValue, 1, 1);
+        GridPane.setConstraints(imageView, 2, 1);
+        GridPane.setConstraints(rating, 0, 2);
+        GridPane.setConstraints(ratingValue, 1, 2);
+        grid2.getChildren().addAll(category, categoryValue, company, companyValue, imageView, rating, ratingValue);
+
+        vbox.getChildren().addAll(grid2Caption, grid2, new Separator());
+
+        //grid3 places the child by influencing the rows and columns
+        //via GridRowInfo and GridColumnInfo. This grid uses the percentages
+        Label grid3Caption = new Label("The example below shows GridPane content placement by influencing row and column percentages.  Also, grid lines are made visible in this example.  The lines can be helpful in debugging.");
+        grid3Caption.setWrapText(true);
+        GridPane grid3 = new GridPane();