Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

GROOVY-5714: Add support for @DelegatesTo using a parameter as delegate

  • Loading branch information...
commit 5fe0df9e353f56fc65924e35ae68d6a0048ba40d 1 parent 5d9ca7f
@melix melix authored
View
8 src/main/groovy/lang/DelegatesTo.java
@@ -45,4 +45,12 @@
public @interface DelegatesTo {
Class value();
int strategy() default Closure.OWNER_FIRST;
+
+ String target() default "";
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @java.lang.annotation.Target({ElementType.PARAMETER})
+ public static @interface Target {
+ String value() default ""; // optional id
+ }
}
View
33 src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -69,6 +69,7 @@
private static final MethodNode GET_OWNER = CLOSURE_TYPE.getGetterMethod("getOwner");
private static final MethodNode GET_THISOBJECT = CLOSURE_TYPE.getGetterMethod("getThisObject");
private static final ClassNode DELEGATES_TO = ClassHelper.make(DelegatesTo.class);
+ private static final ClassNode DELEGATES_TO_TARGET = ClassHelper.make(DelegatesTo.Target.class);
public static final MethodNode CLOSURE_CALL_NO_ARG;
public static final MethodNode CLOSURE_CALL_ONE_ARG;
@@ -1713,14 +1714,36 @@ private void visitMethodCallArguments(ArgumentListExpression arguments, boolean
for (AnnotationNode annotation : annotations) {
// in theory, there can only be one annotation of that type
Expression value = annotation.getMember("value");
+ Expression strategy = annotation.getMember("strategy");
+ Integer stInt = Closure.OWNER_FIRST;
+ if (strategy!=null) {
+ stInt = (Integer) evaluateExpression(new CastExpression(ClassHelper.Integer_TYPE,strategy));
+ }
if (value instanceof ClassExpression) {
- Expression strategy = annotation.getMember("strategy");
- Integer stInt = Closure.OWNER_FIRST;
- if (strategy!=null) {
- stInt = (Integer) evaluateExpression(new CastExpression(ClassHelper.Integer_TYPE,strategy));
- }
// temporarily store the delegation strategy and the delegate type
expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(value.getType(), stInt, delegationMetadata));
+ } else {
+ Expression parameter = annotation.getMember("target");
+ String parameterName = parameter!=null && parameter instanceof ConstantExpression?parameter.getText():"";
+ // todo: handle vargs!
+ for (int j = 0, paramsLength = params.length; j < paramsLength; j++) {
+ final Parameter methodParam = params[j];
+ List<AnnotationNode> targets = methodParam.getAnnotations(DELEGATES_TO_TARGET);
+ if (targets != null && targets.size() == 1) {
+ AnnotationNode targetAnnotation = targets.get(0); // @DelegatesTo.Target Obj foo
+ Expression idMember = targetAnnotation.getMember("value");
+ String id = idMember != null && idMember instanceof ConstantExpression ? idMember.getText() : "";
+ if (id.equals(parameterName)) {
+ if (j < expressionsSize) {
+ Expression actualArgument = expressions.get(j);
+ expression.putNodeMetaData(StaticTypesMarker.DELEGATION_METADATA, new DelegationMetadata(getType(actualArgument), stInt, delegationMetadata));
+ }
+ }
+ }
+ }
+ if (expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA)==null) {
+ addError("Not enough arguments found for a @DelegatesTo method call. Please check that you either use an explicit class or @DelegatesTo.Target with a correct id", arguments);
+ }
}
}
}
View
86 src/test/groovy/transform/stc/DelegatesToSTCTest.groovy
@@ -16,6 +16,8 @@
+
+
package groovy.transform.stc
/**
@@ -214,4 +216,88 @@ class DelegatesToSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ void testShouldDelegateToParameter() {
+ assertScript '''
+ class Foo {
+ boolean called = false
+ def foo() { called = true }
+ }
+
+ def with(@DelegatesTo.Target Object target, @DelegatesTo Closure arg) {
+ arg.delegate = target
+ arg()
+ }
+
+ def test() {
+ def obj = new Foo()
+ with(obj) { foo() }
+ assert obj.called
+ }
+ test()
+ '''
+ }
+
+ void testShouldDelegateToParameterUsingExplicitId() {
+ assertScript '''
+ class Foo {
+ boolean called = false
+ def foo() { called = true }
+ }
+
+ def with(@DelegatesTo.Target('target') Object target, @DelegatesTo(target='target') Closure arg) {
+ arg.delegate = target
+ arg()
+ }
+
+ def test() {
+ def obj = new Foo()
+ with(obj) { foo() }
+ assert obj.called
+ }
+ test()
+ '''
+ }
+
+ void testShouldFailDelegateToParameterUsingWrongId() {
+ shouldFailWithMessages '''
+ class Foo {
+ boolean called = false
+ def foo() { called = true }
+ }
+
+ def with(@DelegatesTo.Target('target') Object target, @DelegatesTo(target='wrongTarget') Closure arg) {
+ arg.delegate = target
+ arg()
+ }
+
+ def test() {
+ def obj = new Foo()
+ with(obj) { foo() }
+ assert obj.called
+ }
+ test()
+ ''', 'Not enough arguments found for a @DelegatesTo method call', 'Cannot find matching method'
+ }
+
+ void testShouldFailDelegateToParameterIfNoTargetSpecified() {
+ shouldFailWithMessages '''
+ class Foo {
+ boolean called = false
+ def foo() { called = true }
+ }
+
+ def with(Object target, @DelegatesTo Closure arg) {
+ arg.delegate = target
+ arg()
+ }
+
+ def test() {
+ def obj = new Foo()
+ with(obj) { foo() }
+ assert obj.called
+ }
+ test()
+ ''', 'Not enough arguments found for a @DelegatesTo method call', 'Cannot find matching method'
+ }
+
}
View
2  src/test/org/codehaus/groovy/classgen/asm/sc/DelegatesToStaticCompileTest.groovy
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+
package org.codehaus.groovy.classgen.asm.sc
import groovy.transform.stc.DelegatesToSTCTest
Please sign in to comment.
Something went wrong with that request. Please try again.