Repository: groovy
Updated Branches:
  refs/heads/master 295dfbd53 -> b6d5a80b5


GROOVY-7769: Allow @Delegate Annotation on a getter (some doco tweaks - closes 
#265)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/b6d5a80b
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/b6d5a80b
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/b6d5a80b

Branch: refs/heads/master
Commit: b6d5a80b558187e445fcbcc6baf13d3e0614e60f
Parents: 295dfbd
Author: paulk <pa...@asert.com.au>
Authored: Fri Apr 22 18:29:10 2016 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Fri Apr 22 18:29:10 2016 +1000

----------------------------------------------------------------------
 src/main/groovy/lang/Delegate.java              | 50 ++++++++++----------
 .../transform/DelegateASTTransformation.java    |  7 +--
 src/spec/doc/core-metaprogramming.adoc          | 24 +++++++++-
 .../test/ClassDesignASTTransformsTest.groovy    | 27 +++++++++++
 4 files changed, 74 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/b6d5a80b/src/main/groovy/lang/Delegate.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/lang/Delegate.java 
b/src/main/groovy/lang/Delegate.java
index f333336..3d418c7 100644
--- a/src/main/groovy/lang/Delegate.java
+++ b/src/main/groovy/lang/Delegate.java
@@ -27,12 +27,12 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * Annotation to automatically delegate part of the functionality of an owner 
class to the
- * annotated delegation target, i. e. a field or getter method's return value.
+ * Annotation to automatically delegate part of the functionality of an owner 
class to the
+ * annotated delegation target. The target can be a field (or property) or a 
method's return value.
  * <p>
- * The delegate type is either the type of the annotated field or the return 
type of
- * the annotated getter method.
- * All public instance methods present in the delegate type and not present in 
the owner class
+ * The delegate type is either the type of the annotated field (or property) 
or the return type of
+ * the annotated method. The method can be thought of as a getter or factory 
method for the delegate.
+ * All public instance methods present in the delegate type and not present in 
the owner class
  * will be added to owner class at compile time. The implementation of such 
automatically added
  * methods is code which calls through to the delegate as per the normal 
delegate pattern.
  * <p>
@@ -65,7 +65,7 @@ import java.lang.annotation.Target;
  * </pre>
  *
  * By default, the owner class will also be modified to implement any 
interfaces
- * implemented by the delegate type. So, in the example above, because {@code 
Date}
+ * implemented by the delegate type. So, in the example above, because {@code 
Date}
  * implements {@code Cloneable} the following will be true:
  *
  * <pre>
@@ -84,9 +84,9 @@ import java.lang.annotation.Target;
  * assert !(gr8conf instanceof Cloneable)
  * </pre>
  *
- * If multiple delegation targets are used and the same method signature occurs
- * in more than one of the respective delegate types, then the delegate will be
- * made to the first defined target having that signature. If this does occur,
+ * If multiple delegation targets are used and the same method signature occurs
+ * in more than one of the respective delegate types, then the delegate will be
+ * made to the first defined target having that signature. If this does occur,
  * it might be regarded as a smell (or at least poor style) and it might be
  * clearer to do the delegation by long hand.
  * <p>
@@ -114,34 +114,32 @@ import java.lang.annotation.Target;
  * <li>Static methods, synthetic methods or methods from the 
<code>GroovyObject</code> interface
  * are not candidates for delegation</li>
  * <li>Non-abstract non-static methods defined in the owner class or its 
superclasses take
- * precedence over methods with identical signatures from a {@code @Delegate} 
target</li>
+ * precedence over methods with identical signatures from a {@code @Delegate} 
target</li>
  * <li>All methods defined in the owner class (including static, abstract or 
private etc.)
- * take precedence over methods with identical signatures from a {@code 
@Delegate} target</li>
+ * take precedence over methods with identical signatures from a {@code 
@Delegate} target</li>
  * <li>Recursive delegation to your own class is not allowed</li>
  * <li>Mixing of {@code @Delegate} with default method arguments is known not 
to work in some cases.
  * We recommend not using these features together.</li>
- * <li>When the delegate type is an interface, the {@code deprecated} 
attribute will be
+ * <li>When the delegate type is an interface, the {@code deprecated} 
attribute will be
  * ignored if the owner class implements that interface (i.e. you must set 
{@code interfaces=false}
  * if you want the {@code deprecated} attribute to be used). Otherwise, the 
resulting class would
  * not compile anyway without manually adding in any deprecated methods in the 
interface.</li>
+ * <li>{@code @Delegate} can work in combination with {@code @Lazy} when 
annotating a field (or property)</li>
  * </ul>
- *
- * @author Alex Tkachman
- * @author Paul King
  */
 @java.lang.annotation.Documented
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD, ElementType.METHOD})
+@Target({ElementType.FIELD, ElementType.METHOD})
 
@GroovyASTTransformationClass("org.codehaus.groovy.transform.DelegateASTTransformation")
 public @interface Delegate {
     /**
-     * @return true if owner class should implement interfaces implemented by 
delegate type
+     * @return true if owner class should implement interfaces implemented by 
delegate type
      */
     boolean interfaces() default true;
 
     /**
      * Whether to apply the delegate pattern to deprecated methods; to avoid 
compilation
-     * errors, this is ignored if the type of the delegate target is an 
interface and
+     * errors, this is ignored if the type of the delegate target is an 
interface and
      * {@code interfaces=true}.
      *
      * @return true if owner class should delegate to methods annotated with 
@Deprecated
@@ -209,12 +207,12 @@ public @interface Delegate {
      * @since 2.3.0
      */
     Class[] includeTypes() default {Undefined.CLASS.class};
-
-    /**
-     * Whether to apply the delegate pattern to all methods, including those 
with names that are considered internal.
-     *
-     * @return true if owner class should delegate to methods which have 
internal names
-     * @since 2.5.0
-     */
-    boolean allNames() default false;
+
+    /**
+     * Whether to apply the delegate pattern to all methods, including those 
with names that are considered internal.
+     *
+     * @return true if owner class should delegate to methods which have 
internal names
+     * @since 2.5.0
+     */
+    boolean allNames() default false;
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/b6d5a80b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
----------------------------------------------------------------------
diff --git 
a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java 
b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
index 231f020..03cbd57 100644
--- a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -66,11 +66,6 @@ import static 
org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGener
 
 /**
  * Handles generation of code for the <code>@Delegate</code> annotation
- *
- * @author Alex Tkachman
- * @author Guillaume Laforge
- * @author Paul King
- * @author Andre Steingress
  */
 @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
 public class DelegateASTTransformation extends AbstractASTTransformation {
@@ -287,7 +282,7 @@ public class DelegateASTTransformation extends 
AbstractASTTransformation {
             boolean alsoLazy = 
!delegate.delegate.getAnnotations(LAZY_TYPE).isEmpty();
             // addMethod will ignore attempts to override abstract or static 
methods with same signature on self
             MethodCallExpression mce = callX(
-                    // use propX when lazy, because lazy is only allowed on 
fields
+                    // use propX when lazy, because lazy is only allowed on 
fields/properties
                     alsoLazy ? propX(varX("this"), delegate.name.substring(1)) 
: delegate.getOp,
                     candidate.getName(),
                     args);

http://git-wip-us.apache.org/repos/asf/groovy/blob/b6d5a80b/src/spec/doc/core-metaprogramming.adoc
----------------------------------------------------------------------
diff --git a/src/spec/doc/core-metaprogramming.adoc 
b/src/spec/doc/core-metaprogramming.adoc
index 565b204..90f228b 100644
--- a/src/spec/doc/core-metaprogramming.adoc
+++ b/src/spec/doc/core-metaprogramming.adoc
@@ -1434,8 +1434,8 @@ The `@Delegate` AST transformation aims at implementing 
the delegation design pa
 
include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegating_class,indent=0]
 ----
 
-The `when` field is annotated with `@Delegate`, meaning that the `Event` class 
will delegate calls to `Date` methods
-to the `when` field. In this case, the generated code looks like this:
+The `when` property is annotated with `@Delegate`, meaning that the `Event` 
class will delegate calls to `Date` methods
+to the `when` property. In this case, the generated code looks like this:
 
 [source,groovy]
 ----
@@ -1449,6 +1449,26 @@ Then you can call the `before` method, for example, 
directly on the `Event` clas
 
include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegation_assert,indent=0]
 ----
 
+Instead of annotating a property (or field), you can also annotate a method.
+In this case, the method can be thought of as a getter or factory method for 
the delegate.
+As an example, here is a class which (rather unusually) has a pool of 
delegates which are
+accessed in a round-robin fashion:
+
+[source,groovy]
+----
+include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_method,indent=0]
+----
+
+Here is an example usage of that class:
+
+[source,groovy]
+----
+include::{projectdir}/src/spec/test/ClassDesignASTTransformsTest.groovy[tags=delegate_method_usage,indent=0]
+----
+
+Using a standard list in this round-robin fashion would violate many expected 
properties of lists, so
+don't expect the above class to do anything useful beyond this trivial example.
+
 The behavior of the `@Delegate` AST transformation can be changed using the 
following parameters:
 
 [cols="1,1,2,3a",options="header"]

http://git-wip-us.apache.org/repos/asf/groovy/blob/b6d5a80b/src/spec/test/ClassDesignASTTransformsTest.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/ClassDesignASTTransformsTest.groovy 
b/src/spec/test/ClassDesignASTTransformsTest.groovy
index 41cbb08..6da827d 100644
--- a/src/spec/test/ClassDesignASTTransformsTest.groovy
+++ b/src/spec/test/ClassDesignASTTransformsTest.groovy
@@ -83,6 +83,33 @@ d.bar() // fails because of @Deprecated
 '''
 
         assertScript '''
+// tag::delegate_method[]
+class Test {
+    private int robinCount = 0
+    private List<List> items = [[0], [1], [2]]
+
+    @Delegate
+    List getRoundRobinList() {
+        items[robinCount++ % items.size()]
+    }
+
+    void checkItems(List<List> testValue) {
+        assert items == testValue
+    }
+}
+// end::delegate_method[]
+
+// tag::delegate_method_usage[]
+def t = new Test()
+t << 'fee'
+t << 'fi'
+t << 'fo'
+t << 'fum'
+t.checkItems([[0, 'fee', 'fum'], [1, 'fi'], [2, 'fo']])
+// end::delegate_method_usage[]
+'''
+
+        assertScript '''
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;

Reply via email to