You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/05/14 01:59:30 UTC

svn commit: r774601 - in /incubator/pivot/trunk: build.xml tutorials/src/pivot/tutorials/Demo.java wtk/src/pivot/wtkx/BindProcessor.java wtk/src/pivot/wtkx/Bindable.java

Author: tvolkert
Date: Wed May 13 23:59:29 2009
New Revision: 774601

URL: http://svn.apache.org/viewvc?rev=774601&view=rev
Log:
Changed compile=true option from going on the @Load annotation to being an option to BindProcessor, and added a "compilable" attribute to @Load, which can override compile=true on the processor

Modified:
    incubator/pivot/trunk/build.xml
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/Demo.java
    incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java
    incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java

Modified: incubator/pivot/trunk/build.xml
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/build.xml?rev=774601&r1=774600&r2=774601&view=diff
==============================================================================
--- incubator/pivot/trunk/build.xml (original)
+++ incubator/pivot/trunk/build.xml Wed May 13 23:59:29 2009
@@ -470,7 +470,7 @@
             target="${compiler.target}"
             encoding="${compiler.encoding}"
             failonerror="true">
-            <compilerarg line="-Xlint -processor pivot.wtkx.BindProcessor"/>
+            <compilerarg line="-Xlint -processor pivot.wtkx.BindProcessor -Acompile=false"/>
             <classpath>
                 <pathelement location="core/${folder.bin}"/>
                 <pathelement location="web/${folder.bin}"/>
@@ -512,7 +512,7 @@
             target="${compiler.target}"
             encoding="${compiler.encoding}"
             failonerror="true">
-            <compilerarg line="-Xlint -processor pivot.wtkx.BindProcessor"/>
+            <compilerarg line="-Xlint -processor pivot.wtkx.BindProcessor -Acompile=false"/>
             <classpath>
                 <pathelement location="core/${folder.bin}"/>
                 <pathelement location="web/${folder.bin}"/>

Modified: incubator/pivot/trunk/tutorials/src/pivot/tutorials/Demo.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/Demo.java?rev=774601&r1=774600&r2=774601&view=diff
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/Demo.java (original)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/Demo.java Wed May 13 23:59:29 2009
@@ -97,7 +97,7 @@
     }
 
     private class ButtonsRollupStateHandler extends RollupStateHandler {
-        @Load(name="buttons.wtkx", compile=true)
+        @Load(name="buttons.wtkx")
         private Component component;
 
         public Vote previewExpandedChange(Rollup rollup) {
@@ -213,7 +213,7 @@
     }
 
     private class SplittersRollupStateHandler extends RollupStateHandler {
-        @Load(name="splitters.wtkx", compile=true) private Component component;
+        @Load(name="splitters.wtkx") private Component component;
 
         public Vote previewExpandedChange(Rollup rollup) {
             if (component == null) {
@@ -293,7 +293,7 @@
     }
 
     private class MetersRollupStateHandler extends RollupStateHandler {
-        @Load(name="meters.wtkx", compile=true) private Component component;
+        @Load(name="meters.wtkx") private Component component;
         @Bind(property="component") private ActivityIndicator activityIndicator1;
         @Bind(property="component") private ActivityIndicator activityIndicator2;
         @Bind(property="component") private ActivityIndicator activityIndicator3;
@@ -447,7 +447,7 @@
     }
 
     private class TreesRollupStateHandler extends RollupStateHandler {
-        @Load(name="trees.wtkx", compile=true) private Component component;
+        @Load(name="trees.wtkx") private Component component;
         @Bind(property="component") private TreeView editableTreeView;
 
         public Vote previewExpandedChange(Rollup rollup) {
@@ -464,7 +464,7 @@
     }
 
     private class DragDropRollupStateHandler extends RollupStateHandler {
-        @Load(name="dragdrop.wtkx", compile=true) private Component component;
+        @Load(name="dragdrop.wtkx") private Component component;
         @Bind(property="component") private ImageView imageView1;
         @Bind(property="component") private ImageView imageView2;
         @Bind(property="component") private ImageView imageView3;
@@ -591,7 +591,7 @@
     }
 
     private class AlertsRollupStateHandler extends RollupStateHandler {
-        @Load(name="alerts.wtkx", compile=true) private Component component;
+        @Load(name="alerts.wtkx") private Component component;
         @Bind(property="component") private PushButton alertButton;
         @Bind(property="component") private PushButton promptButton;
 
@@ -675,7 +675,7 @@
         }
     }
 
-    @Load(name="demo.wtkx", compile=true) private Window window;
+    @Load(name="demo.wtkx") private Window window;
     @Bind(property="window") private Rollup buttonsRollup;
     @Bind(property="window") private Rollup listsRollup;
     @Bind(property="window") private Rollup textRollup;

Modified: incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java?rev=774601&r1=774600&r2=774601&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java Wed May 13 23:59:29 2009
@@ -24,6 +24,7 @@
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedOptions;
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
@@ -50,29 +51,58 @@
 import com.sun.source.util.Trees;
 
 /**
- * Annotation processor that may be run on classes that use the bind
- * annotations (<tt>@Load</tt> and <tt>@Bind</tt>) in order to cause the WTKX
- * binding process to use compiled code as opposed to the Java Reflection API.
- * Callers may wish to do this, for example, if they plan to run their Pivot
- * application in an unsigned applet, since the reflective bind process
- * requires security privileges not granted to un-trusted applets.
+ * Annotation processor that may be run on classes that use the
+ * <tt>@Load</tt> and <tt>@Bind</tt> annotations in order to cause the
+ * WTKX binding process to avoid security-constrained reflection calls. Callers
+ * will typically want to do this if they plan to run their Pivot application
+ * in an unsigned applet, since the reflective bind process requires security
+ * privileges not granted to un-trusted applets.
  * <p>
- * In order to compile using this annotation processor, add the
- * <tt>-processor pivot.wtkx.BindProcessor</tt> argument to your <tt>javac</tt>
- * command. Note that this class utilizes classes specific to Sun's
- * <tt>javac</tt> implementation, and as such, it will only work with a Sun
- * <tt>javac</tt> compiler.
+ * <b>Note</b>: this class utilizes classes specific to Sun's <tt>javac</tt>
+ * implementation, and as such, it will only work with a Sun <tt>javac</tt>
+ * compiler.
+ * <h3>Options:</h3>
+ * As an optimization, this processor supports a <tt>compile</tt> option. When
+ * this option is set to <tt>true</tt>, the WTKX will be compiled into the
+ * class and loaded via compiled code (as opposed to loaded at runtime using
+ * {@link WTKXSerializer}). If unspecified, the WTKX loading will be done at
+ * runtime.
  * <p>
+ * There are some considerations when using the <tt>compile=true</tt>
+ * option. Namely:
+ * <ol>
+ *   <li>
+ *     WTKX URL resolution syntax (<tt>"&#64;relative/path.png"</tt>)
+ *     will load relative URLs relative to the <tt>Bindable</tt>
+ *     subclass (as opposed to relative to the WTKX file, which is
+ *     normally the case). It is therefore recommended that when this
+ *     option is used, your WTKX file should live in the same directory
+ *     as your <tt>Bindable</tt> subclass to eliminate any ambiguity.
+ *   </li>
+ *   <li>
+ *     This option may render the WTKX file superfluous at runtime since its
+ *     contents are compiled directly into the class. In such cases, callers
+ *     may choose to exclude the WTKX file from their JAR file.
+ *   </li>
+ * </ol>
+ * <h3>Usage:</h3>
+ * To use this annotation processor at the command line, pass the following
+ * options to <tt>javac</tt>:
+ * <pre>
+ *     -processor pivot.wtkx.BindProcessor [-Acompile=&lt;boolean&gt;]
+ * </pre>
  * To use this annotation processor with Ant, add the following line to your
  * Ant <tt>javac</tt> task:
- * <p>
  * <pre>
-       &lt;compilerarg line="-processor pivot.wtkx.BindProcessor"/&gt;
+ *     &lt;compilerarg line="-processor pivot.wtkx.BindProcessor [-Acompile=&lt;boolean&gt;]"/&gt;
  * </pre>
  *
  * @author tvolkert
+ * @see Bindable.Load
+ * @see Bindable.Bind
  */
 @SupportedAnnotationTypes("pivot.wtkx.*")
+@SupportedOptions("compile")
 @SupportedSourceVersion(SourceVersion.RELEASE_6)
 public class BindProcessor extends AbstractProcessor {
     /**
@@ -92,8 +122,8 @@
          * @author tvolkert
          */
         public static class LoadGroup {
-            public JCVariableDecl loadField = null;
-            public ArrayList<JCVariableDecl> bindFields = null;
+            public final JCVariableDecl loadField;
+            public final ArrayList<JCVariableDecl> bindFields = new ArrayList<JCVariableDecl>();
 
             private LoadGroup(JCVariableDecl loadField) {
                 this.loadField = loadField;
@@ -190,11 +220,6 @@
                 added = true;
                 LoadGroup loadGroup = loadGroups.get(loadFieldName);
 
-                if (loadGroup.bindFields == null) {
-                    // Lazily create the bind fields list
-                    loadGroup.bindFields = new ArrayList<JCVariableDecl>();
-                }
-
                 // Add this bind field to its load group
                 loadGroup.bindFields.add(bindField);
             }
@@ -255,254 +280,51 @@
             super.visitClassDef(classDeclaration);
             stack.pop();
 
-            // Reconcile the dossier
+            // See if any relevant information was recorded in the dossier
             annotationDossier.reconcile();
-
             Map<String, AnnotationDossier.LoadGroup> loadGroups = annotationDossier.getLoadGroups();
             List<JCVariableDecl> strandedBindFields = annotationDossier.getStrandedBindFields();
 
             if (loadGroups != null || strandedBindFields != null) {
-                // There is some bind work to be done in this class; start by
-                // creating the source code buffer
-                StringBuilder buf = new StringBuilder("class _A {");
+                // There is some bind work to be done in this class
+                StringBuilder buf = new StringBuilder("class _TMP {");
                 buf.append("@Override @SuppressWarnings(\"unchecked\") ");
                 buf.append("protected void bind(pivot.collections.Dictionary<String,");
                 buf.append("pivot.collections.Dictionary<String, Object>> namedObjectDictionaries) {");
                 buf.append("super.bind(namedObjectDictionaries);");
 
-                buf.append("pivot.wtkx.WTKXSerializer wtkxSerializer;");
+                // Local variable declarations
                 buf.append("Object object;");
+                buf.append("pivot.wtkx.WTKXSerializer wtkxSerializer;");
+                buf.append("java.net.URL location;");
+                buf.append("java.util.Locale locale;");
+                buf.append("pivot.util.Resources resources;");
 
+                // Process @Load fields
                 if (loadGroups != null) {
-                    // Process WTKX loads in this class
-                    buf.append("java.net.URL location;");
-                    buf.append("java.util.Locale locale;");
-                    buf.append("pivot.util.Resources resources;");
-
                     for (String loadFieldName : loadGroups) {
                         AnnotationDossier.LoadGroup loadGroup = loadGroups.get(loadFieldName);
                         JCVariableDecl loadField = loadGroup.loadField;
                         JCAnnotation loadAnnotation = getLoadAnnotation(loadField);
+                        Boolean compilable = getAnnotationProperty(loadAnnotation, "compilable");
 
-                        String resourceName = getAnnotationProperty(loadAnnotation, "name");
-                        String baseName = getAnnotationProperty(loadAnnotation, "resources");
-                        String language = getAnnotationProperty(loadAnnotation, "locale");
-                        Boolean compile = getAnnotationProperty(loadAnnotation, "compile");
-                        boolean defaultResources = (baseName == null);
-
-                        if (DEBUG) {
-                            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                                String.format("Processing load(%s, %s#%s)", resourceName,
-                                classDeclaration.name.toString(), loadFieldName));
-                        }
-
-                        if (defaultResources) {
-                            baseName = classDeclaration.name.toString();
-                            if ("".equals(baseName)) {
-                                baseName = null;
-                            }
-                        }
-
-                        if (compile != null && compile.booleanValue()) {
-                            FileObject sourceFile = null;
-                            if (classDeclaration.sym != null) {
-                                sourceFile = classDeclaration.sym.sourcefile;
-                            }
-
-                            if (sourceFile != null) {
-                                JavaFileManager fileManager = context.get(JavaFileManager.class);
-
-                                InputStream inputStream;
-                                try {
-                                    if (resourceName.startsWith("/")) {
-                                        resourceName = resourceName.substring(1);
-                                        ClassLoader classLoader = fileManager.getClassLoader
-                                            (StandardLocation.SOURCE_PATH);
-                                        URL resourceLocation = classLoader.getResource(resourceName);
-                                        inputStream = new BufferedInputStream(resourceLocation.openStream());
-                                    } else {
-                                        String packageName = classDeclaration.sym.packge().toString();
-                                        FileObject resourceFile = fileManager.getFileForInput
-                                            (StandardLocation.SOURCE_PATH, packageName, resourceName);
-                                        inputStream = resourceFile.openInputStream();
-                                    }
-
-                                    try {
-                                        // TODO Handle resources
-                                        WTKXSerializer wtkxSerializer = new WTKXSerializer();
-                                        String blockCode = wtkxSerializer.readSource(inputStream);
-
-                                        // Open local scope for variable name protection
-                                        buf.append("{");
-
-                                        // Add interpreted code
-                                        buf.append(blockCode);
-
-                                        // Public and protected fields get kept for subclasses
-                                        if ((loadField.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
-                                            buf.append(String.format
-                                                ("namedObjectDictionaries.put(\"%s\", __namedObjects);",
-                                                loadFieldName));
-                                        }
-
-                                        // Bind @Load member
-                                        buf.append(String.format
-                                            ("%s = (%s)__result;", loadFieldName, loadField.vartype.toString()));
-
-                                        // Bind @Bind members
-                                        if (loadGroup.bindFields != null) {
-                                            for (JCVariableDecl bindField : loadGroup.bindFields) {
-                                                String bindFieldName = bindField.name.toString();
-                                                JCAnnotation bindAnnotation = getBindAnnotation(bindField);
-
-                                                String bindName = getAnnotationProperty(bindAnnotation, "name");
-                                                if (bindName == null) {
-                                                    // The bind name defaults to the field name
-                                                    bindName = bindFieldName;
-                                                }
-
-                                                buf.append(String.format
-                                                    ("object = __namedObjects.get(\"%s\");", bindName));
-                                                buf.append
-                                                    ("if (object == null) ");
-                                                buf.append(String.format
-                                                    ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");",
-                                                    bindName));
-                                                buf.append(String.format
-                                                    ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
-                                            }
-                                        }
-
-                                        // Close local scope
-                                        buf.append("}");
-                                    } finally {
-                                        inputStream.close();
-                                    }
-                                } catch (Exception ex) {
-                                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                                    "Error trying to load field " + classDeclaration.name.toString() + "." +
-                                    loadFieldName + ": " + ex.toString());
-                                }
-                            } else {
-                                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-                                    "Unable to determine source file location for " +
-                                    classDeclaration.name.toString());
-                            }
+                        if (compile && compilable != Boolean.FALSE) {
+                            processCompiledLoad(buf, classDeclaration, loadField, loadGroup.bindFields);
                         } else {
-                            // Attempt to load the resources
-                            buf.append("resources = null;");
-                            if (baseName != null) {
-                                if (language == null) {
-                                    buf.append("locale = java.util.Locale.getDefault();");
-                                } else {
-                                    buf.append(String.format
-                                        ("locale = new java.util.Locale(\"%s\");", language));
-                                }
-                                buf.append("try {");
-                                buf.append(String.format
-                                    ("resources = new pivot.util.Resources(%s, locale, \"UTF8\");",
-                                    defaultResources ? (baseName + ".class.getName()") : ("\"" + baseName + "\"")));
-                                buf.append("} catch(java.io.IOException ex) {");
-                                buf.append("throw new pivot.wtkx.BindException(ex);");
-                                buf.append("} catch (pivot.serialization.SerializationException ex) {");
-                                buf.append("throw new pivot.wtkx.BindException(ex);");
-                                buf.append("} catch (java.util.MissingResourceException ex) {");
-                                if (!defaultResources) {
-                                    buf.append("throw new pivot.wtkx.BindException(ex);");
-                                }
-                                buf.append("}");
-                            }
-
-                            // Load the WTKX resource
-                            buf.append("wtkxSerializer = new pivot.wtkx.WTKXSerializer(resources);");
-                            buf.append(String.format("location = getClass().getResource(\"%s\");", resourceName));
-                            buf.append("try {");
-                            buf.append("object = wtkxSerializer.readObject(location);");
-                            buf.append("} catch (Exception ex) {");
-                            buf.append("throw new pivot.wtkx.BindException(ex);");
-                            buf.append("}");
-
-                            // Bind the resource to the field
-                            buf.append(String.format
-                                ("%s = (%s)object;", loadFieldName, loadField.vartype.toString()));
-
-                            // Public and protected fields get kept for subclasses
-                            if ((loadField.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
-                                buf.append(String.format
-                                    ("namedObjectDictionaries.put(\"%s\", wtkxSerializer.getNamedObjects());",
-                                    loadFieldName));
-                            }
-
-                            // Bind the resource lookups to their corresponding fields
-                            if (loadGroup.bindFields != null) {
-                                for (JCVariableDecl bindField : loadGroup.bindFields) {
-                                    String bindFieldName = bindField.name.toString();
-                                    JCAnnotation bindAnnotation = getBindAnnotation(bindField);
-
-                                    String bindName = getAnnotationProperty(bindAnnotation, "name");
-                                    if (bindName == null) {
-                                        // The bind name defaults to the field name
-                                        bindName = bindFieldName;
-                                    }
-
-                                    if (DEBUG) {
-                                        String property = getAnnotationProperty(bindAnnotation, "property");
-                                        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
-                                            String.format("Processing bind(%s.%s, %s#%s)", property,
-                                            bindName, classDeclaration.name.toString(), bindFieldName));
-                                    }
-
-                                    buf.append(String.format
-                                        ("object = wtkxSerializer.getObjectByName(\"%s\");", bindName));
-                                    buf.append
-                                        ("if (object == null) ");
-                                    buf.append(String.format
-                                        ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
-                                    buf.append(String.format
-                                        ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
-                                }
-                            }
+                            processRuntimeLoad(buf, classDeclaration, loadField, loadGroup.bindFields);
                         }
                     }
                 }
 
+                // Process @Bind fields bound to superclass @Load fields
                 if (strandedBindFields != null) {
-                    // Process binds to superclass-loaded fields
-                    buf.append("pivot.collections.Dictionary<String, Object> namedObjects;");
-
-                    for (JCVariableDecl bindField : strandedBindFields) {
-                        String bindFieldName = bindField.name.toString();
-                        JCAnnotation bindAnnotation = getBindAnnotation(bindField);
-                        String loadFieldName = getAnnotationProperty(bindAnnotation, "property");
-
-                        String bindName = getAnnotationProperty(bindAnnotation, "name");
-                        if (bindName == null) {
-                            // The bind name defaults to the field name
-                            bindName = bindFieldName;
-                        }
-
-                        buf.append(String.format
-                            ("namedObjects = namedObjectDictionaries.get(\"%s\");", loadFieldName));
-
-                        buf.append
-                            ("if (namedObjects == null) {");
-                        buf.append(String.format
-                            ("throw new pivot.wtkx.BindException(\"Property not found: %s.\");", loadFieldName));
-                        buf.append
-                            ("}");
-
-                        buf.append(String.format
-                            ("object = namedObjects.get(\"%s\");", bindName));
-                        buf.append
-                            ("if (object == null) ");
-                        buf.append(String.format
-                            ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
-                        buf.append(String.format
-                            ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
-                    }
+                    processStrandedBinds(buf, strandedBindFields);
                 }
 
+                // Close method declaration
                 buf.append("}");
+
+                // Close _TMP class declaration
                 buf.append("}");
 
                 // Parse the source code and extract the method declaration
@@ -512,7 +334,7 @@
                 JCClassDecl parsedClassDeclaration = (JCClassDecl)parsedCompilationUnit.defs.head;
                 JCMethodDecl parsedMethodDeclaration = (JCMethodDecl)parsedClassDeclaration.defs.head;
 
-                // Add the AST method declaration to our class
+                // Add the method declaration to our class
                 classDeclaration.defs = classDeclaration.defs.prepend(parsedMethodDeclaration);
             }
         }
@@ -523,15 +345,15 @@
          * information in the current bind scope, to be used before we exit
          * the containing class.
          *
-         * @param variableDeclaration
+         * @param field
          * The AST variable declaration node
          */
         @Override
-        public void visitVarDef(JCVariableDecl variableDeclaration) {
-            super.visitVarDef(variableDeclaration);
+        public void visitVarDef(JCVariableDecl field) {
+            super.visitVarDef(field);
 
-            JCAnnotation loadAnnotation = getLoadAnnotation(variableDeclaration);
-            JCAnnotation bindAnnotation = getBindAnnotation(variableDeclaration);
+            JCAnnotation loadAnnotation = getLoadAnnotation(field);
+            JCAnnotation bindAnnotation = getBindAnnotation(field);
 
             if (loadAnnotation != null
                 && bindAnnotation != null) {
@@ -540,18 +362,276 @@
                     + " and " + Bindable.Bind.class.getName() + " annotations.");
             } else if (loadAnnotation != null) {
                 AnnotationDossier annotationDossier = stack.peek();
-                annotationDossier.createLoadGroup(variableDeclaration);
+                annotationDossier.createLoadGroup(field);
 
                 // Increment the tally for reporting purposes
                 loadTally++;
             } else if (bindAnnotation != null) {
                 AnnotationDossier annotationDossier = stack.peek();
-                annotationDossier.addToLoadGroup(variableDeclaration);
+                annotationDossier.addToLoadGroup(field);
 
                 // Increment the tally for reporting purposes
                 bindTally++;
             }
         }
+
+        /**
+         * Processes an <tt>@Load</tt> field and associated <tt>@Bind</tt>
+         * fields into compiled code.
+         *
+         * @param buf
+         * The buffer into which to write the source code
+         *
+         * @param classDeclaration
+         * The AST node of the class that contains the <tt>@Load</tt> field
+         *
+         * @param loadField
+         * The AST node with the <tt>@Load</tt> annotation
+         *
+         * @param bindFields
+         * List of AST nodes with the <tt>@Bind</tt> annotations that are
+         * associated with the load field
+         */
+        private void processCompiledLoad(StringBuilder buf, JCClassDecl classDeclaration,
+            JCVariableDecl loadField, ArrayList<JCVariableDecl> bindFields) {
+            FileObject sourceFile = null;
+            if (classDeclaration.sym != null) {
+                sourceFile = classDeclaration.sym.sourcefile;
+            }
+
+            if (sourceFile != null) {
+                JavaFileManager fileManager = context.get(JavaFileManager.class);
+
+                String loadFieldName = loadField.name.toString();
+
+                // Get annotation properties
+                JCAnnotation loadAnnotation = getLoadAnnotation(loadField);
+                String resourceName = getAnnotationProperty(loadAnnotation, "name");
+
+                InputStream inputStream;
+                try {
+                    if (resourceName.startsWith("/")) {
+                        resourceName = resourceName.substring(1);
+                        ClassLoader classLoader = fileManager.getClassLoader
+                            (StandardLocation.SOURCE_PATH);
+                        URL resourceLocation = classLoader.getResource(resourceName);
+                        inputStream = new BufferedInputStream(resourceLocation.openStream());
+                    } else {
+                        String packageName = classDeclaration.sym.packge().toString();
+                        FileObject resourceFile = fileManager.getFileForInput
+                            (StandardLocation.SOURCE_PATH, packageName, resourceName);
+                        inputStream = resourceFile.openInputStream();
+                    }
+
+                    try {
+                        // TODO Handle resources
+                        WTKXSerializer wtkxSerializer = new WTKXSerializer();
+                        String blockCode = wtkxSerializer.readSource(inputStream);
+
+                        // Open local scope for variable name protection
+                        buf.append("{");
+
+                        // Add interpreted code
+                        buf.append(blockCode);
+
+                        // Public and protected fields get kept for subclasses
+                        if ((loadField.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
+                            buf.append(String.format
+                                ("namedObjectDictionaries.put(\"%s\", __namedObjects);",
+                                loadFieldName));
+                        }
+
+                        // Bind @Load member
+                        buf.append(String.format
+                            ("%s = (%s)__result;", loadFieldName, loadField.vartype.toString()));
+
+                        // Bind @Bind members
+                        for (JCVariableDecl bindField : bindFields) {
+                            String bindFieldName = bindField.name.toString();
+                            JCAnnotation bindAnnotation = getBindAnnotation(bindField);
+
+                            String bindName = getAnnotationProperty(bindAnnotation, "name");
+                            if (bindName == null) {
+                                // The bind name defaults to the field name
+                                bindName = bindFieldName;
+                            }
+
+                            buf.append(String.format
+                                ("object = __namedObjects.get(\"%s\");", bindName));
+                            buf.append
+                                ("if (object == null) ");
+                            buf.append(String.format
+                                ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");",
+                                bindName));
+                            buf.append(String.format
+                                ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
+                        }
+
+                        // Close local scope
+                        buf.append("}");
+                    } finally {
+                        inputStream.close();
+                    }
+                } catch (Exception ex) {
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                        "Error trying to load field " + classDeclaration.name.toString() + "." +
+                        loadFieldName + ": " + ex.toString());
+                }
+            } else {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                    "Unable to determine source file location for " +
+                    classDeclaration.name.toString());
+            }
+        }
+
+        /**
+         * Processes an <tt>@Load</tt> field and associated <tt>@Bind</tt>
+         * fields into runtime code.
+         *
+         * @param buf
+         * The buffer into which to write the source code
+         *
+         * @param classDeclaration
+         * The AST node of the class that contains the <tt>@Load</tt> field
+         *
+         * @param loadField
+         * The AST node with the <tt>@Load</tt> annotation
+         *
+         * @param bindFields
+         * List of AST nodes with the <tt>@Bind</tt> annotations that are
+         * associated with the load field
+         */
+        private void processRuntimeLoad(StringBuilder buf, JCClassDecl classDeclaration,
+            JCVariableDecl loadField, ArrayList<JCVariableDecl> bindFields) {
+            String loadFieldName = loadField.name.toString();
+
+            // Get annotation properties
+            JCAnnotation loadAnnotation = getLoadAnnotation(loadField);
+            String resourceName = getAnnotationProperty(loadAnnotation, "name");
+            String baseName = getAnnotationProperty(loadAnnotation, "resources");
+            String language = getAnnotationProperty(loadAnnotation, "locale");
+
+            // Default the Resources baseName to the class name
+            boolean defaultResources = false;
+            if (baseName == null) {
+                defaultResources = true;
+                if (!classDeclaration.name.isEmpty()) {
+                    baseName = classDeclaration.name.toString();
+                }
+            }
+
+            // Attempt to load the resources
+            buf.append("resources = null;");
+            if (baseName != null) {
+                if (language == null) {
+                    buf.append("locale = java.util.Locale.getDefault();");
+                } else {
+                    buf.append(String.format
+                        ("locale = new java.util.Locale(\"%s\");", language));
+                }
+                buf.append("try {");
+                buf.append(String.format
+                    ("resources = new pivot.util.Resources(%s, locale, \"UTF8\");",
+                    defaultResources ? (baseName + ".class.getName()") : ("\"" + baseName + "\"")));
+                buf.append("} catch(java.io.IOException ex) {");
+                buf.append("throw new pivot.wtkx.BindException(ex);");
+                buf.append("} catch (pivot.serialization.SerializationException ex) {");
+                buf.append("throw new pivot.wtkx.BindException(ex);");
+                buf.append("} catch (java.util.MissingResourceException ex) {");
+                if (!defaultResources) {
+                    buf.append("throw new pivot.wtkx.BindException(ex);");
+                }
+                buf.append("}");
+            }
+
+            // Load the WTKX resource
+            buf.append("wtkxSerializer = new pivot.wtkx.WTKXSerializer(resources);");
+            buf.append(String.format("location = getClass().getResource(\"%s\");", resourceName));
+            buf.append("try {");
+            buf.append("object = wtkxSerializer.readObject(location);");
+            buf.append("} catch (Exception ex) {");
+            buf.append("throw new pivot.wtkx.BindException(ex);");
+            buf.append("}");
+
+            // Bind the resource to the field
+            buf.append(String.format
+                ("%s = (%s)object;", loadFieldName, loadField.vartype.toString()));
+
+            // Public and protected fields get kept for subclasses
+            if ((loadField.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
+                buf.append(String.format
+                    ("namedObjectDictionaries.put(\"%s\", wtkxSerializer.getNamedObjects());",
+                    loadFieldName));
+            }
+
+            // Process @Bind variables
+            for (JCVariableDecl bindField : bindFields) {
+                String bindFieldName = bindField.name.toString();
+                JCAnnotation bindAnnotation = getBindAnnotation(bindField);
+
+                String bindName = getAnnotationProperty(bindAnnotation, "name");
+                if (bindName == null) {
+                    // The bind name defaults to the field name
+                    bindName = bindFieldName;
+                }
+
+                buf.append(String.format
+                    ("object = wtkxSerializer.getObjectByName(\"%s\");", bindName));
+                buf.append
+                    ("if (object == null) ");
+                buf.append(String.format
+                    ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
+                buf.append(String.format
+                    ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
+            }
+        }
+
+        /**
+         * Processes a list of <tt>@Bind</tt> fields that were not associated
+         * with any <tt>@Load</tt> field in their class. Such fields are called
+         * stranded bind fields and are assumed to be associated with an
+         * <tt>@Load</tt> field in a superclass.
+         *
+         * @param buf
+         * The buffer into which to write the source code
+         *
+         * @param strandedBindFields
+         * The list of <tt>@Bind</tt> fields
+         */
+        private void processStrandedBinds(StringBuilder buf, List<JCVariableDecl> strandedBindFields) {
+            buf.append("pivot.collections.Dictionary<String, Object> namedObjects;");
+
+            for (JCVariableDecl bindField : strandedBindFields) {
+                String bindFieldName = bindField.name.toString();
+                JCAnnotation bindAnnotation = getBindAnnotation(bindField);
+                String loadFieldName = getAnnotationProperty(bindAnnotation, "property");
+
+                String bindName = getAnnotationProperty(bindAnnotation, "name");
+                if (bindName == null) {
+                    // The bind name defaults to the field name
+                    bindName = bindFieldName;
+                }
+
+                buf.append(String.format
+                    ("namedObjects = namedObjectDictionaries.get(\"%s\");", loadFieldName));
+
+                buf.append
+                    ("if (namedObjects == null) {");
+                buf.append(String.format
+                    ("throw new pivot.wtkx.BindException(\"Property not found: %s.\");", loadFieldName));
+                buf.append
+                    ("}");
+
+                buf.append(String.format
+                    ("object = namedObjects.get(\"%s\");", bindName));
+                buf.append
+                    ("if (object == null) ");
+                buf.append(String.format
+                    ("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
+                buf.append(String.format
+                    ("%s = (%s)object;", bindFieldName, bindField.vartype.toString()));
+            }
+        }
     }
 
     private int loadTally = 0;
@@ -561,9 +641,9 @@
     private Context context;
     private Scanner.Factory scannerFactory;
     private Parser.Factory parserFactory;
-    private BindInjector bindInjector = new BindInjector();
 
-    private static final boolean DEBUG = false;
+    private BindInjector bindInjector = new BindInjector();
+    private boolean compile = false;
 
     @Override
     public synchronized void init(ProcessingEnvironment processingEnvironment) {
@@ -573,10 +653,16 @@
         context = ((JavacProcessingEnvironment)processingEnvironment).getContext();
         scannerFactory = Scanner.Factory.instance(context);
         parserFactory = Parser.Factory.instance(context);
+
+        String compileOption = processingEnvironment.getOptions().get("compile");
+        if (compileOption != null) {
+            compile = Boolean.parseBoolean(compileOption);
+        }
     }
 
     @Override
-    public boolean process(java.util.Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
+    public boolean process(java.util.Set<? extends TypeElement> annotations,
+        RoundEnvironment roundEnvironment) {
         if (!roundEnvironment.processingOver()) {
             for (Element rootElement : roundEnvironment.getRootElements()) {
                 if (rootElement.getKind() == ElementKind.CLASS) {
@@ -599,37 +685,37 @@
      * Gets the <tt>Load</tt> AST annotation node that's associated with
      * the specified AST variable declaration node.
      *
-     * @param variableDeclaration
+     * @param field
      * The AST variable declaration node
      *
      * @return
      * The AST annotation node, or <tt>null</tt> if no such annotation is
      * associated with the variable declaration
      */
-    private static JCAnnotation getLoadAnnotation(JCVariableDecl variableDeclaration) {
-        return getAnnotation(variableDeclaration, Bindable.Load.class.getSimpleName());
+    private static JCAnnotation getLoadAnnotation(JCVariableDecl field) {
+        return getAnnotation(field, Bindable.Load.class.getSimpleName());
     }
 
     /**
      * Gets the <tt>Bind</tt> AST annotation node that's associated with
      * the specified AST variable declaration node.
      *
-     * @param variableDeclaration
+     * @param field
      * The AST variable declaration node
      *
      * @return
      * The AST annotation node, or <tt>null</tt> if no such annotation is
      * associated with the variable declaration
      */
-    private static JCAnnotation getBindAnnotation(JCVariableDecl variableDeclaration) {
-        return getAnnotation(variableDeclaration, Bindable.Bind.class.getSimpleName());
+    private static JCAnnotation getBindAnnotation(JCVariableDecl field) {
+        return getAnnotation(field, Bindable.Bind.class.getSimpleName());
     }
 
     /**
      * Gets the AST annotation node with the given name that's associated
      * with the specified AST variable declaration node.
      *
-     * @param variableDeclaration
+     * @param field
      * The AST variable declaration node
      *
      * @param name
@@ -639,12 +725,12 @@
      * The AST annotation node, or <tt>null</tt> if no such annotation is
      * associated with the variable declaration
      */
-    private static JCAnnotation getAnnotation(JCVariableDecl variableDeclaration, String name) {
+    private static JCAnnotation getAnnotation(JCVariableDecl field, String name) {
         JCAnnotation result = null;
 
-        if (variableDeclaration.mods != null
-            && variableDeclaration.mods.annotations != null) {
-            for (JCAnnotation annotation : variableDeclaration.mods.annotations) {
+        if (field.mods != null
+            && field.mods.annotations != null) {
+            for (JCAnnotation annotation : field.mods.annotations) {
                 JCIdent identifier = (JCIdent)annotation.annotationType;
 
                 if (identifier.name.contentEquals(name)) {

Modified: incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java?rev=774601&r1=774600&r2=774601&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/Bindable.java Wed May 13 23:59:29 2009
@@ -104,22 +104,20 @@
  *   <li>
  *     <b>Compiled</b>
  *     <br/><br/>
- *     As a performance optimization, a <tt>compile=true</tt> option is
- *     available in the {@link Load @Load} annotation. If this option is
- *     combined with the annotation processor, the WTKX will be compiled into
- *     the class and loaded via compiled code. This method is suitable for
- *     callers who are comfortable with the dependency implied by the annotation
- *     processor (outlined above) and comfortable with the specifics of the
- *     {@link Load#compile() compile=true} option.
+ *     As a performance optimization, <tt>BindProcessor</tt> supports a
+ *     <tt>compile=true</tt> option. This option will cause the WTKX to be
+ *     compiled into the class and loaded via compiled code. This method is
+ *     suitable for callers who are comfortable with the dependency implied by
+ *     the annotation processor (outlined above) and comfortable with the
+ *     specifics of the <tt>compile=true</tt> option. See the
+ *     <tt>BindProcessor</tt> class for more details.
  *     <br/><br/>
  *   </li>
  * </ol>
  *
- * @see
- * BindProcessor
- *
  * @author gbrown
  * @author tvolkert
+ * @see BindProcessor
  */
 public abstract class Bindable {
     /**
@@ -159,37 +157,11 @@
         public String locale() default "\0";
 
         /**
-         * Indicates whether the loaded WTKX should be compiled into the class
-         * or if it should be loaded at runtime via the <tt>WTKXSerializer</tt>
-         * class. If unspecified, the WTKX loading will be done at runtime.
-         * <p>
-         * There are some considerations when using the <tt>compile=true</tt>
-         * option. Namely:
-         * <ol>
-         *   <li>
-         *     This option only has meaning when the annotations are processed
-         *     during compilation using {@link BindProcessor}. Callers who
-         *     forego use of the annotation processor will always be using a
-         *     runtime implementation of WTKX loading, and in such cases, the
-         *     <tt>compile</tt> flag will be ignored.
-         *   </li>
-         *   <li>
-         *     This option may render the WTKX file superfluous at runtime
-         *     since its contents are compiled directly into the class. In such
-         *     cases, callers may choose to exclude the WTKX file from their
-         *     JAR file.
-         *   </li>
-         *   <li>
-         *     WTKX URL resolution syntax (<tt>"&#64;relative/path.png"</tt>)
-         *     will load relative URLs relative to the <tt>Bindable</tt>
-         *     subclass (as opposed to relative to the WTKX file, which is
-         *     normally the case). It is therefore recommended that when this
-         *     option is used, your WTKX file should live in the same directory
-         *     as your <tt>Bindable</tt> subclass to eliminate any ambiguity.
-         *   </li>
-         * </ol>
+         * Indicates whether the WTKX resource is a compilable resource. Set
+         * this to <tt>false</tt> if your WTKX is dynamically generated at
+         * runtime. Defaults to <tt>true</tt>.
          */
-        public boolean compile() default false;
+        public boolean compilable() default true;
     }
 
     /**