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/08 19:02:17 UTC
svn commit: r773048 -
/incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java
Author: tvolkert
Date: Fri May 8 17:02:17 2009
New Revision: 773048
URL: http://svn.apache.org/viewvc?rev=773048&view=rev
Log:
Updated BindProcessor to corectly handle the locale and resources attributes of the @Load annotation
Modified:
incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java
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=773048&r1=773047&r2=773048&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/BindProcessor.java Fri May 8 17:02:17 2009
@@ -32,6 +32,8 @@
import pivot.collections.ArrayList;
import pivot.collections.ArrayStack;
import pivot.collections.HashMap;
+import pivot.collections.List;
+import pivot.collections.Map;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
@@ -43,9 +45,9 @@
import com.sun.source.util.Trees;
/**
- * Annotation processor that injects <tt>bind()</tt> overrides into classes
- * that use the <tt>@Load</tt> and <tt>@Bind</tt> annotations to perform WTKX
- * loading and binding.
+ * Annotation processor that injects <tt>__bind__(Map)</tt> overrides into
+ * classes that use the <tt>@Load</tt> and <tt>@Bind</tt> annotations to
+ * perform WTKX loading and binding.
*
* @author tvolkert
*/
@@ -54,44 +56,77 @@
public class BindProcessor extends AbstractProcessor {
/**
* Holds pertinent information about a class' member variables that use
- * the <tt>@Load</tt> and <tt>@Bind</tt> annotations. A bind scope object
+ * the <tt>@Load</tt> and <tt>@Bind</tt> annotations. A dossier object
* is pushed onto a stack before visiting a class and popped off the
* stack after visiting it, allowing us to know if any members variables
- * contained in the class need bind processing as we're exiting the class.
+ * contained in the class need processing.
*
* @author tvolkert
*/
- private static class BindScope {
+ private static class AnnotationDossier {
+ /**
+ * Encapsulates a load field and the bind fields that are bound to that
+ * load field.
+ *
+ * @author tvolkert
+ */
public static class LoadGroup {
- public JCVariableDecl loadVariableDeclaration = null;
- public ArrayList<JCVariableDecl> bindVariableDeclarations = null;
+ public JCVariableDecl loadField = null;
+ public ArrayList<JCVariableDecl> bindFields = null;
- private LoadGroup(JCVariableDecl loadVariableDeclaration) {
- this.loadVariableDeclaration = loadVariableDeclaration;
+ private LoadGroup(JCVariableDecl loadField) {
+ this.loadField = loadField;
}
}
- // Maps load field names to their corresponding load group
- public HashMap<String, LoadGroup> loadGroups = null;
+ private HashMap<String, LoadGroup> loadGroups = null;
+ private ArrayList<JCVariableDecl> strandedBindFields = null;
- // Bind fields whose load field hasn't been seen yet
- public ArrayList<JCVariableDecl> strandedBindVariableDeclarations = null;
+ /**
+ * Gets the load groups that have been recorded in this dossier,
+ * indexed by load field name.
+ *
+ * @return
+ * The load groups map, or <tt>null</tt> if no load groups have been
+ * recorded in this dossier
+ */
+ public Map<String, LoadGroup> getLoadGroups() {
+ return loadGroups;
+ }
+
+ /**
+ * Gets the bind fields that were recorded in this dossier whose
+ * associated load fields were missing from the dossier. When bind
+ * fields are first recorded, they can be stranded if they appear in
+ * the source file before their associated load field (since the source
+ * file is processed linearly). Calling {@link reconcile()} after all
+ * fields have been visited will pair these stranded bind fields up
+ * with their associated load group and remove them from the stranded
+ * list. After <tt>reconcile</tt> has been called, any bind fields that
+ * remain in the stranded list are assumed to be bound to
+ * <tt>public</tt> or <tt>protected</tt> load fields in a superclass.
+ * It is up to the <tt>__bind__</tt> method to handle these stranded
+ * bind fields correctly.
+ */
+ public List<JCVariableDecl> getStrandedBindFields() {
+ return strandedBindFields;
+ }
/**
* Creates a load group for the specified load field.
*
- * @param loadVariableDeclaration
+ * @param loadField
* The AST load variable declaration node
*/
- public void createLoadGroup(JCVariableDecl loadVariableDeclaration) {
+ public void createLoadGroup(JCVariableDecl loadField) {
if (loadGroups == null) {
// Lazily create the load groups map
loadGroups = new HashMap<String, LoadGroup>();
}
// Create a new load group for this load field
- String loadFieldName = loadVariableDeclaration.name.toString();
- loadGroups.put(loadFieldName, new LoadGroup(loadVariableDeclaration));
+ String loadFieldName = loadField.name.toString();
+ loadGroups.put(loadFieldName, new LoadGroup(loadField));
}
/**
@@ -100,11 +135,11 @@
* encountered by the bind injector visitor, it is added to the
* stranded list.
*
- * @param loadVariableDeclaration
+ * @param loadField
* The AST load variable declaration node
*/
- public void addToLoadGroup(JCVariableDecl bindVariableDeclaration) {
- addToLoadGroup(bindVariableDeclaration, true);
+ public void addToLoadGroup(JCVariableDecl bindField) {
+ addToLoadGroup(bindField, true);
}
/**
@@ -113,7 +148,7 @@
* encountered by the bind injector visitor, and <tt>recordStranded</tt>
* is <tt>true</tt>, then it is added to the stranded list.
*
- * @param loadVariableDeclaration
+ * @param loadField
* The AST load variable declaration node
*
* @param recordStranded
@@ -123,10 +158,10 @@
* <tt>true</tt> if the field was added to a load group; <tt>false</tt>
* if it was not
*/
- private boolean addToLoadGroup(JCVariableDecl bindVariableDeclaration, boolean recordStranded) {
+ private boolean addToLoadGroup(JCVariableDecl bindField, boolean recordStranded) {
boolean added = false;
- JCAnnotation bindAnnotation = getBindAnnotation(bindVariableDeclaration);
+ JCAnnotation bindAnnotation = getBindAnnotation(bindField);
String loadFieldName = getAnnotationProperty(bindAnnotation, "property");
if (loadGroups != null
@@ -134,22 +169,22 @@
added = true;
LoadGroup loadGroup = loadGroups.get(loadFieldName);
- if (loadGroup.bindVariableDeclarations == null) {
+ if (loadGroup.bindFields == null) {
// Lazily create the bind fields list
- loadGroup.bindVariableDeclarations = new ArrayList<JCVariableDecl>();
+ loadGroup.bindFields = new ArrayList<JCVariableDecl>();
}
// Add this bind field to its load group
- loadGroup.bindVariableDeclarations.add(bindVariableDeclaration);
+ loadGroup.bindFields.add(bindField);
}
if (!added && recordStranded) {
- if (strandedBindVariableDeclarations == null) {
+ if (strandedBindFields == null) {
// Lazily create the stranded list
- strandedBindVariableDeclarations = new ArrayList<JCVariableDecl>();
+ strandedBindFields = new ArrayList<JCVariableDecl>();
}
- strandedBindVariableDeclarations.add(bindVariableDeclaration);
+ strandedBindFields.add(bindField);
}
return added;
@@ -162,12 +197,12 @@
* left in the stranded list are assumed to be binding to a superclass'
* load field.
*/
- public void resolve() {
- if (strandedBindVariableDeclarations != null) {
- for (int i = strandedBindVariableDeclarations.getLength() - 1; i >= 0; i--) {
- if (addToLoadGroup(strandedBindVariableDeclarations.get(i), false)) {
+ public void reconcile() {
+ if (strandedBindFields != null) {
+ for (int i = strandedBindFields.getLength() - 1; i >= 0; i--) {
+ if (addToLoadGroup(strandedBindFields.get(i), false)) {
// Remove it from the stranded list
- strandedBindVariableDeclarations.remove(i, 1);
+ strandedBindFields.remove(i, 1);
}
}
}
@@ -180,27 +215,32 @@
* @author tvolkert
*/
private class BindInjector extends TreeTranslator {
- private ArrayStack<BindScope> bindScopeStack = new ArrayStack<BindScope>();
+ private ArrayStack<AnnotationDossier> stack = new ArrayStack<AnnotationDossier>();
/**
- * Injects an override implementation of the <tt>bind(Map)</tt> method
- * into the specified class if any member variables are found to be
- * annotated with the <tt>@Load</tt> or <tt>@Bind</tt> annotations.
+ * Injects an override implementation of the <tt>__bind__(Map)</tt>
+ * method into the specified class if any member variables are found to
+ * be annotated with the <tt>@Load</tt> or <tt>@Bind</tt> annotations.
*
- * @param tree
+ * @param classDeclaration
* The AST class declaration node
*/
@Override
- public void visitClassDef(JCClassDecl tree) {
- BindScope bindScope = new BindScope();
+ public void visitClassDef(JCClassDecl classDeclaration) {
+ AnnotationDossier annotationDossier = new AnnotationDossier();
- bindScopeStack.push(bindScope);
- super.visitClassDef(tree);
- bindScopeStack.pop();
-
- bindScope.resolve();
- if (bindScope.loadGroups != null
- || bindScope.strandedBindVariableDeclarations != null) {
+ // Visit all of the class' nodes to record a full dossier
+ stack.push(annotationDossier);
+ super.visitClassDef(classDeclaration);
+ stack.pop();
+
+ // Reconcile 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 sourceCode = new StringBuilder("class _A {");
@@ -212,22 +252,60 @@
sourceCode.append("pivot.wtkx.WTKXSerializer wtkxSerializer;");
sourceCode.append("Object object;");
sourceCode.append("java.net.URL location;");
+ sourceCode.append("java.util.Locale locale;");
+ sourceCode.append("pivot.util.Resources resources;");
+
+ if (loadGroups != null) {
+ for (String loadFieldName : loadGroups) {
+ AnnotationDossier.LoadGroup loadGroup = loadGroups.get(loadFieldName);
+ JCVariableDecl loadField = loadGroup.loadField;
+ JCAnnotation loadAnnotation = getLoadAnnotation(loadField);
- if (bindScope.loadGroups != null) {
- for (String loadFieldName : bindScope.loadGroups) {
- BindScope.LoadGroup loadGroup = bindScope.loadGroups.get(loadFieldName);
- JCVariableDecl loadVariableDeclaration = loadGroup.loadVariableDeclaration;
- JCAnnotation loadAnnotation = getLoadAnnotation(loadVariableDeclaration);
String resourceName = getAnnotationProperty(loadAnnotation, "name");
+ String baseName = getAnnotationProperty(loadAnnotation, "resources");
+ String language = getAnnotationProperty(loadAnnotation, "locale");
+ boolean defaultResources = (baseName == null);
if (DEBUG) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
String.format("Processing load(%s, %s#%s)", resourceName,
- tree.name.toString(), loadFieldName));
+ classDeclaration.name.toString(), loadFieldName));
+ }
+
+ if (defaultResources) {
+ baseName = classDeclaration.name.toString();
+ if ("".equals(baseName)) {
+ baseName = null;
+ }
+ }
+
+ if (baseName != null) {
+ // Attempt to load the resources
+ if (language == null) {
+ sourceCode.append("locale = java.util.Locale.getDefault();");
+ } else {
+ sourceCode.append(String.format("locale = new java.util.Locale(\"%s\");", language));
+ }
+ sourceCode.append("resources = null;");
+ sourceCode.append("try {");
+ sourceCode.append(String.format("resources = new pivot.util.Resources(\"%s\", locale, \"UTF8\");", baseName));
+ sourceCode.append("} catch(java.io.IOException ex) {");
+ sourceCode.append("throw new pivot.wtkx.BindException(ex);");
+ sourceCode.append("} catch (pivot.serialization.SerializationException ex) {");
+ sourceCode.append("throw new pivot.wtkx.BindException(ex);");
+ sourceCode.append("} catch (java.util.MissingResourceException ex) {");
+ if (!defaultResources) {
+ sourceCode.append("throw new pivot.wtkx.BindException(ex);");
+ }
+ sourceCode.append("}");
}
// Load the WTKX resource
- sourceCode.append("wtkxSerializer = new pivot.wtkx.WTKXSerializer();");
+ if (baseName == null) {
+ sourceCode.append("wtkxSerializer = new pivot.wtkx.WTKXSerializer();");
+ } else {
+ sourceCode.append("wtkxSerializer = new pivot.wtkx.WTKXSerializer(resources);");
+ }
sourceCode.append(String.format("location = getClass().getResource(\"%s\");", resourceName));
sourceCode.append("try {");
sourceCode.append("object = wtkxSerializer.readObject(location);");
@@ -237,19 +315,19 @@
// Bind the resource to the field
sourceCode.append(String.format("%s = (%s)object;", loadFieldName,
- loadVariableDeclaration.vartype.toString()));
+ loadField.vartype.toString()));
// Public and protected fields get kept for subclasses
- if ((loadVariableDeclaration.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
+ if ((loadField.mods.flags & (Flags.PUBLIC | Flags.PROTECTED)) != 0) {
sourceCode.append(String.format("namedSerializers.put(\"%s\", wtkxSerializer);",
loadFieldName));
}
// Bind the resource lookups to their corresponding fields
- if (loadGroup.bindVariableDeclarations != null) {
- for (JCVariableDecl bindVariableDeclaration : loadGroup.bindVariableDeclarations) {
- String bindFieldName = bindVariableDeclaration.name.toString();
- JCAnnotation bindAnnotation = getBindAnnotation(bindVariableDeclaration);
+ 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) {
@@ -261,7 +339,7 @@
String property = getAnnotationProperty(bindAnnotation, "property");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
String.format("Processing bind(%s.%s, %s#%s)", property,
- bindName, tree.name.toString(), bindFieldName));
+ bindName, classDeclaration.name.toString(), bindFieldName));
}
sourceCode.append(String.format("object = wtkxSerializer.getObjectByName(\"%s\");",
@@ -270,16 +348,16 @@
sourceCode.append(String.format("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
sourceCode.append("}");
sourceCode.append(String.format("%s = (%s)object;", bindFieldName,
- bindVariableDeclaration.vartype.toString()));
+ bindField.vartype.toString()));
}
}
}
}
- if (bindScope.strandedBindVariableDeclarations != null) {
- for (JCVariableDecl bindVariableDeclaration : bindScope.strandedBindVariableDeclarations) {
- String bindFieldName = bindVariableDeclaration.name.toString();
- JCAnnotation bindAnnotation = getBindAnnotation(bindVariableDeclaration);
+ if (strandedBindFields != null) {
+ for (JCVariableDecl bindField : strandedBindFields) {
+ String bindFieldName = bindField.name.toString();
+ JCAnnotation bindAnnotation = getBindAnnotation(bindField);
String loadFieldName = getAnnotationProperty(bindAnnotation, "property");
String bindName = getAnnotationProperty(bindAnnotation, "name");
@@ -301,7 +379,7 @@
sourceCode.append(String.format("throw new pivot.wtkx.BindException(\"Element not found: %s.\");", bindName));
sourceCode.append("}");
sourceCode.append(String.format("%s = (%s)object;", bindFieldName,
- bindVariableDeclaration.vartype.toString()));
+ bindField.vartype.toString()));
}
}
@@ -311,12 +389,12 @@
// Parse the source code and extract the method declaration
Scanner scanner = scannerFactory.newScanner(sourceCode.toString());
Parser parser = parserFactory.newParser(scanner, false, false);
- JCCompilationUnit compilationUnit = parser.compilationUnit();
- JCClassDecl classDeclaration = (JCClassDecl)compilationUnit.defs.head;
- JCMethodDecl methodDeclaration = (JCMethodDecl)classDeclaration.defs.head;
+ JCCompilationUnit parsedCompilationUnit = parser.compilationUnit();
+ JCClassDecl parsedClassDeclaration = (JCClassDecl)parsedCompilationUnit.defs.head;
+ JCMethodDecl parsedMethodDeclaration = (JCMethodDecl)parsedClassDeclaration.defs.head;
// Add the AST method declaration to our class
- tree.defs = tree.defs.prepend(methodDeclaration);
+ classDeclaration.defs = classDeclaration.defs.prepend(parsedMethodDeclaration);
}
}
@@ -326,7 +404,7 @@
* information in the current bind scope, to be used before we exit
* the containing class.
*
- * @param tree
+ * @param variableDeclaration
* The AST variable declaration node
*/
@Override
@@ -342,14 +420,14 @@
"Cannot combine " + Bindable.Load.class.getName()
+ " and " + Bindable.Bind.class.getName() + " annotations.");
} else if (loadAnnotation != null) {
- BindScope bindScope = bindScopeStack.peek();
- bindScope.createLoadGroup(variableDeclaration);
+ AnnotationDossier annotationDossier = stack.peek();
+ annotationDossier.createLoadGroup(variableDeclaration);
// Increment the tally for reporting purposes
loadTally++;
} else if (bindAnnotation != null) {
- BindScope bindScope = bindScopeStack.peek();
- bindScope.addToLoadGroup(variableDeclaration);
+ AnnotationDossier annotationDossier = stack.peek();
+ annotationDossier.addToLoadGroup(variableDeclaration);
// Increment the tally for reporting purposes
bindTally++;