Index: src/main/org/codehaus/groovy/control/ResolveVisitor.java
===================================================================
--- src/main/org/codehaus/groovy/control/ResolveVisitor.java (revision 6351)
+++ src/main/org/codehaus/groovy/control/ResolveVisitor.java (working copy)
@@ -79,6 +79,7 @@
import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
import org.codehaus.groovy.ast.stmt.ThrowStatement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.classgen.BuiltinAnnotations;
import org.codehaus.groovy.classgen.Verifier;
import org.codehaus.groovy.control.messages.ExceptionMessage;
import org.codehaus.groovy.syntax.Types;
@@ -226,6 +227,7 @@
resolveFromDefaultImports(type,testDefaultImports) ||
resolveFromStaticInnerClasses(type,testStaticInnerClasses) ||
resolveFromClassCache(type) ||
+ resolveFromBuiltinAnnotations(type) ||
resolveToClass(type) ||
resolveToScript(type);
@@ -370,6 +372,16 @@
return false;
}
+ private boolean resolveFromBuiltinAnnotations(ClassNode type) {
+ for (int i = 0; i < BuiltinAnnotations.ALL_BUILTIN_ANNOTATIONS.length; i++) {
+ AnnotationNode candidateBuiltin = BuiltinAnnotations.ALL_BUILTIN_ANNOTATIONS[i];
+ if (candidateBuiltin.getClassNode().getName().equals(type.getName())) {
+ type.setRedirect(candidateBuiltin.getClassNode());
+ return true;
+ }
+ }
+ return false;
+ }
private void setClass(ClassNode n, Class cls) {
ClassNode cn = ClassHelper.make(cls);
Index: src/main/org/codehaus/groovy/ast/ClassNode.java
===================================================================
--- src/main/org/codehaus/groovy/ast/ClassNode.java (revision 6351)
+++ src/main/org/codehaus/groovy/ast/ClassNode.java (working copy)
@@ -37,10 +37,17 @@
import groovy.lang.GroovyObject;
import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.objectweb.asm.Opcodes;
@@ -49,6 +56,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -98,6 +106,7 @@
private List methods = new ArrayList();
private List fields = new ArrayList();
private List properties = new ArrayList();
+ private boolean addedPropertyChangeSupport;
private Map fieldIndex = new HashMap();
private ModuleNode module;
private CompileUnit compileUnit;
@@ -128,7 +137,7 @@
/**
* Returns the ClassNode this ClassNode is redirecting to.
*/
- protected ClassNode redirect(){
+ public ClassNode redirect(){
if (redirect==null) return this;
return redirect.redirect();
}
@@ -1023,4 +1032,107 @@
public void setGenericsTypes(GenericsType[] genericsTypes) {
this.genericsTypes = genericsTypes;
}
+
+ public boolean addPropertyChangeSupport() {
+ //TODO look for this$propertyChangeSupport in parent and devline to add if present
+ if (!addedPropertyChangeSupport) {
+ ClassNode pcsClassNode = ClassHelper.make(java.beans.PropertyChangeSupport.class);
+ ClassNode pclClassNode = ClassHelper.make(java.beans.PropertyChangeListener.class);
+
+ // add field protected static PropertyChangeSupport this$propertyChangeSupport = new java.beans.PropertyChangeSupport(this)
+ addField(
+ "this$propertyChangeSupport",
+ ACC_FINAL | ACC_PROTECTED | ACC_SYNTHETIC,
+ pcsClassNode,
+ new ConstructorCallExpression(pcsClassNode,
+ new ArgumentListExpression(new Expression[] {new VariableExpression("this")})));
+
+
+ // add method void addPropertyChangeListener(listner) {
+ // this$propertyChangeSupport.addPropertyChangeListner(listener)
+ // }
+ addMethod(
+ new MethodNode(
+ "addPropertyChangeListener",
+ ACC_PUBLIC | ACC_SYNTHETIC,
+ ClassHelper.VOID_TYPE,
+ new Parameter[] { new Parameter(pclClassNode, "listener")},
+ ClassNode.EMPTY_ARRAY,
+ new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(getField("this$propertyChangeSupport")),
+ "addPropertyChangeListener",
+ new ArgumentListExpression(
+ new Expression[] {new VariableExpression("listener")})))));
+ // add method void addPropertyChangeListener(name, listner) {
+ // this$propertyChangeSupport.addPropertyChangeListner(name, listener)
+ // }
+ addMethod(
+ new MethodNode(
+ "addPropertyChangeListener",
+ ACC_PUBLIC | ACC_SYNTHETIC,
+ ClassHelper.VOID_TYPE,
+ new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "name"), new Parameter(pclClassNode, "listener")},
+ ClassNode.EMPTY_ARRAY,
+ new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(getField("this$propertyChangeSupport")),
+ "addPropertyChangeListener",
+ new ArgumentListExpression(
+ new Expression[] {new VariableExpression("name"), new VariableExpression("listener")})))));
+
+ // add method boolean removePropertyChangeListener(listner) {
+ // return this$propertyChangeSupport.removePropertyChangeListener(listener);
+ // }
+ addMethod(
+ new MethodNode(
+ "removePropertyChangeListener",
+ ACC_PUBLIC | ACC_SYNTHETIC,
+ ClassHelper.VOID_TYPE,
+ new Parameter[] { new Parameter(pclClassNode, "listener")},
+ ClassNode.EMPTY_ARRAY,
+ new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(getField("this$propertyChangeSupport")),
+ "removePropertyChangeListener",
+ new ArgumentListExpression(
+ new Expression[] {new VariableExpression("listener")})))));
+ // add method void removePropertyChangeListener(name, listner)
+ addMethod(
+ new MethodNode(
+ "removePropertyChangeListener",
+ ACC_PUBLIC | ACC_SYNTHETIC,
+ ClassHelper.VOID_TYPE,
+ new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "name"), new Parameter(pclClassNode, "listener")},
+ ClassNode.EMPTY_ARRAY,
+ new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(getField("this$propertyChangeSupport")),
+ "removePropertyChangeListener",
+ new ArgumentListExpression(
+ new Expression[] {new VariableExpression("name"), new VariableExpression("listener")})))));
+ // add PropertyChangeSupport[] getPropertyChangeListeners() {
+ // return propertyChangeSupport.getPropertyChangeListeners
+ // }
+ addMethod(
+ new MethodNode(
+ "getPropertyChangeListeners",
+ ACC_PUBLIC | ACC_SYNTHETIC,
+ pclClassNode.makeArray(),
+ Parameter.EMPTY_ARRAY,
+ ClassNode.EMPTY_ARRAY,
+ new ReturnStatement(
+ new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(getField("this$propertyChangeSupport")),
+ "getPropertyChangeListeners",
+ ArgumentListExpression.EMPTY_ARGUMENTS)))));
+
+
+ addedPropertyChangeSupport = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
}
Index: src/main/org/codehaus/groovy/ast/AnnotationNode.java
===================================================================
--- src/main/org/codehaus/groovy/ast/AnnotationNode.java (revision 6351)
+++ src/main/org/codehaus/groovy/ast/AnnotationNode.java (working copy)
@@ -51,6 +51,7 @@
import java.util.Map;
import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.classgen.BuiltinAnnotations;
/**
@@ -118,11 +119,11 @@
}
public boolean isBuiltIn(){
- return false;
+ return BuiltinAnnotations.ALL_BUILTIN_ANNOTATION_CLASS_NODES.contains(getClassNode().redirect());
}
-
+
/**
- * Flag corresponding to RetentionPolicy.
+ * Flag corresponding to RetentionPolicy.RUNTIME.
* @return true if the annotation should be visible at runtime,
* false otherwise
*/
@@ -142,7 +143,7 @@
public void setRuntimeRetention(boolean flag) {
this.runtimeRetention = flag;
}
-
+
/**
* Flag corresponding to RetentionPolicy.SOURCE.
* @return true if the annotation is only allowed in sources
@@ -154,11 +155,11 @@
/** Sets the internal flag if the current annotation has
* RetentionPolicy.SOURCE.
- */
+ */
public void setSourceRetention(boolean flag) {
this.sourceRetention = flag;
}
-
+
public void setAllowedTargets(int bitmap) {
this.allowedTarges = bitmap;
}
Index: src/main/org/codehaus/groovy/classgen/Verifier.java
===================================================================
--- src/main/org/codehaus/groovy/classgen/Verifier.java (revision 6351)
+++ src/main/org/codehaus/groovy/classgen/Verifier.java (working copy)
@@ -53,6 +53,7 @@
import java.util.Iterator;
import java.util.List;
+import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.CodeVisitorSupport;
@@ -91,7 +92,7 @@
/**
* Verifies the AST node and adds any defaulted AST code before
* bytecode generation occurs.
- *
+ *
* @author James Strachan
* @version $Revision$
*/
@@ -115,16 +116,16 @@
*/
public void visitClass(ClassNode node) {
this.classNode = node;
-
+
if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
- //interfaces have no construcotrs, but this code expects one,
+ //interfaces have no construcotrs, but this code expects one,
//so create a dummy and don't add it to the class node
ConstructorNode dummy = new ConstructorNode(0,null);
addInitialization(node, dummy);
node.visitContents(this);
return;
}
-
+
addDefaultParameterMethods(node);
addDefaultParameterConstructors(node);
@@ -171,13 +172,13 @@
// don't do anything as the base class implements the invokeMethod
if (!addDelegateObject) {
-
+
VariableExpression vMethods = new VariableExpression("method");
VariableExpression vArguments = new VariableExpression("arguments");
VariableScope blockScope = new VariableScope();
blockScope.getReferencedLocalVariables().put("method",vMethods);
blockScope.getReferencedLocalVariables().put("arguments",vArguments);
-
+
node.addSyntheticMethod(
"invokeMethod",
ACC_PUBLIC,
@@ -186,7 +187,7 @@
new Parameter(ClassHelper.STRING_TYPE, "method"),
new Parameter(ClassHelper.OBJECT_TYPE, "arguments")
},
- ClassNode.EMPTY_ARRAY,
+ ClassNode.EMPTY_ARRAY,
new BlockStatement(
new Statement[] {
initMetaClassField,
@@ -206,8 +207,8 @@
blockScope
)
);
-
+
if (!node.isScript()) {
node.addSyntheticMethod(
"getProperty",
@@ -234,7 +235,7 @@
blockScope = new VariableScope();
blockScope.getReferencedLocalVariables().put("property",vProp);
blockScope.getReferencedLocalVariables().put("value",vValue);
-
+
node.addSyntheticMethod(
"setProperty",
ACC_PUBLIC,
@@ -268,7 +269,7 @@
constructor.setSynthetic(true);
node.addConstructor(constructor);
}
-
+
if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
FieldNode timeTagField = new FieldNode(
Verifier.__TIMESTAMP,
@@ -281,11 +282,11 @@
timeTagField.setSynthetic(true);
node.addField(timeTagField);
}
-
- addInitialization(node);
+
checkReturnInObjectInitializer(node.getObjectInitializerStatements());
node.getObjectInitializerStatements().clear();
node.visitContents(this);
+ addInitialization(node);
}
private void checkReturnInObjectInitializer(List init) {
CodeVisitorSupport cvs = new CodeVisitorSupport() {
@@ -319,7 +320,7 @@
String name = expression.getName();
if (!name.equals("this") && !name.equals("super")) return;
throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
- }
+ }
};
Statement s = node.getCode();
//todo why can a statement can be null?
@@ -433,13 +434,13 @@
// Implementation methods
//-------------------------------------------------------------------------
-
+
private interface DefaultArgsAction {
public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
}
-
+
/**
- * Creates a new helper method for each combination of default parameter expressions
+ * Creates a new helper method for each combination of default parameter expressions
*/
protected void addDefaultParameterMethods(final ClassNode node) {
List methods = new ArrayList(node.getMethods());
@@ -457,7 +458,7 @@
}
});
}
-
+
protected void addDefaultParameterConstructors(final ClassNode node) {
List methods = new ArrayList(node.getDeclaredConstructors());
addDefaultParameters(methods, new DefaultArgsAction(){
@@ -471,7 +472,7 @@
}
/**
- * Creates a new helper method for each combination of default parameter expressions
+ * Creates a new helper method for each combination of default parameter expressions
*/
protected void addDefaultParameters(List methods, DefaultArgsAction action) {
for (Iterator iter = methods.iterator(); iter.hasNext();) {
@@ -529,10 +530,10 @@
protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
Statement firstStatement = constructorNode.getFirstStatement();
ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
-
+
// in case of this(...) let the other constructor do the intit
if (first!=null && first.isThisCall()) return;
-
+
List statements = new ArrayList();
List staticStatements = new ArrayList();
for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
@@ -555,7 +556,7 @@
// it is super(..) since this(..) is already covered
otherStatements.remove(0);
statements.add(0, firstStatement);
- }
+ }
statements.addAll(otherStatements);
}
constructorNode.setCode(new BlockStatement(statements, block.getVariableScope()));
@@ -611,6 +612,31 @@
protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
Expression expression = new FieldExpression(field);
+
+ // getAnnotation(String) is indexed by the text in the code, not FQN, so we must iterate
+ // the problem is that the has table is created before names are fully resolved
+ for (Iterator iter = field.getAnnotations().values().iterator(); iter.hasNext(); ) {
+ AnnotationNode annotationNode = (AnnotationNode) iter.next();
+ if ("groovy.lang.BoundProperty".equals(annotationNode.getClassNode().getName())) {
+ // found a bound expression, output bound block
+ field.getDeclaringClass().addPropertyChangeSupport();
+ // add this$propertyChangeSupport.firePropertyChange("field", field, field = value);
+ return new ExpressionStatement(
+ new MethodCallExpression(
+ new FieldExpression(field.getDeclaringClass().getField("this$propertyChangeSupport")),
+ "firePropertyChange",
+ new ArgumentListExpression(
+ new Expression[] {
+ new ConstantExpression(field.getName()),
+ expression,
+ new BinaryExpression(
+ expression,
+ Token.newSymbol(Types.EQUAL, 0, 0),
+ new VariableExpression("value"))})));
+ }
+ }
+
+ // did not create bound block, create unbound block
return new ExpressionStatement(
new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
}
Index: src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java
===================================================================
--- src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java (revision 6351)
+++ src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java (working copy)
@@ -94,6 +94,14 @@
}
public AnnotationNode visit(AnnotationNode node) {
+ // check for builtin replacements
+ for (int i = 0; i < BuiltinAnnotations.ALL_BUILTIN_ANNOTATIONS.length; i++) {
+ AnnotationNode candidateBuiltin = BuiltinAnnotations.ALL_BUILTIN_ANNOTATIONS[i];
+ if (candidateBuiltin.getClassNode() == node.getClassNode()) {
+ return candidateBuiltin;
+ }
+ }
+
if(!isValidAnnotationClass(node)) {
node.setValid(false);
return node;
@@ -271,7 +279,7 @@
}
else if("SOURCE".equals(retentionPolicyEnum.toString())) {
this.annotation.setSourceRetention(true);
- }
+ }
}
private void initializeTarget(Class annotationClass, Class targetClass, Object targetAnnotation) {
Index: src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
===================================================================
--- src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java (revision 6351)
+++ src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java (working copy)
@@ -110,15 +110,21 @@
return;
}
+ boolean builtinsOnly = true;
+ Collection annotations = node.getAnnotations().values();
+ for(Iterator it = annotations.iterator(); builtinsOnly && it.hasNext(); ) {
+ AnnotationNode an = (AnnotationNode)it.next();
+ builtinsOnly &= BuiltinAnnotations.ALL_BUILTIN_ANNOTATION_CLASS_NODES.contains(an.getClassNode().redirect());
+ }
+
this.currentClass.setAnnotated(true);
- if(!isAnnotationCompatible()) {
+ if(!isAnnotationCompatible() && !builtinsOnly) {
addError("Annotations are not supported in the current runtime." + JVM_ERROR_MESSAGE,
node);
return;
}
- Collection annotations = node.getAnnotations().values();
for(Iterator it = annotations.iterator(); it.hasNext(); ) {
AnnotationNode an = (AnnotationNode) it.next();
Index: build.xml
===================================================================
--- build.xml (revision 6351)
+++ build.xml (working copy)
@@ -60,9 +60,19 @@
description="Compile the Java and Groovy code in the main source.">
+
+
+ debug="yes" source="5" target="5" fork="true" classpathref="compilePath">
+
+
+
+
+
+
+