http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/builder/Builder.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/Builder.java 
b/src/main/groovy/groovy/transform/builder/Builder.java
new file mode 100644
index 0000000..93b6090
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/Builder.java
@@ -0,0 +1,160 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.BuilderStrategy;
+
+/**
+ * The {@code @Builder} AST transformation is used to help write classes that 
can be created using <em>fluent</em> api calls.<!-- -->
+ * The transform supports multiple building strategies to cover a range of 
cases and there are a number
+ * of configuration options to customize the building process.
+ *
+ * In addition, a number of annotation attributes let you customise the 
building process. Not all annotation attributes
+ * are supported by all strategies. See the individual strategy documentation 
for more details.
+ * If you're an AST hacker, you can also define your own strategy class.
+ *
+ * The following strategies are bundled with Groovy:
+ * <ul>
+ *     <li>{@link SimpleStrategy} for creating chained setters</li>
+ *     <li>{@link ExternalStrategy} where you annotate an explicit builder 
class while leaving some buildee class being built untouched</li>
+ *     <li>{@link DefaultStrategy} which creates a nested helper class for 
instance creation</li>
+ *     <li>{@link InitializerStrategy} which creates a nested helper class for 
instance creation which when used with {@code @CompileStatic} allows type-safe 
object creation</li>
+ * </ul>
+ *
+ * Note that Groovy provides other built-in mechanisms for easy creation of 
objects, e.g. the named-args constructor:
+ * <pre>
+ * new Person(firstName: "Robert", lastName: "Lewandowski", age: 21)
+ * </pre>
+ * or the with statement:
+ * <pre>
+ * new Person().with {
+ *     firstName = "Robert"
+ *     lastName = "Lewandowski"
+ *     age = 21
+ * }
+ * </pre>
+ * so you might not find value in using the builder transform at all. But if 
you need Java integration or in some cases improved type safety, the {@code 
@Builder} transform might prove very useful.
+ *
+ * @see groovy.transform.builder.SimpleStrategy
+ * @see groovy.transform.builder.ExternalStrategy
+ * @see groovy.transform.builder.DefaultStrategy
+ * @see groovy.transform.builder.InitializerStrategy
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD})
+@GroovyASTTransformationClass("org.codehaus.groovy.transform.BuilderASTTransformation")
+public @interface Builder {
+
+    /**
+     * A class for which builder methods should be created. It will be an 
error to leave
+     * this attribute with its default value for some strategies.
+     */
+    Class forClass() default Undefined.CLASS.class;
+
+    /**
+     * A class capturing the builder strategy
+     */
+    Class<? extends BuilderStrategy> builderStrategy() default 
DefaultStrategy.class;
+
+    /**
+     * The prefix to use when creating the setter methods.
+     * Default is determined by the strategy which might use "" or "set" but 
you can choose your own, e.g. "with".
+     * If non-empty the first letter of the property will be capitalized 
before being appended to the prefix.
+     */
+    String prefix() default Undefined.STRING;
+
+    /**
+     * For strategies which create a builder helper class, the class name to 
use for the helper class.
+     * Not used if using {@code forClass} since in such cases the builder 
class is explicitly supplied.
+     * Default is determined by the strategy, e.g. <em>TargetClass</em> + 
"Builder" or <em>TargetClass</em> + "Initializer".
+     */
+    String builderClassName() default Undefined.STRING;
+
+    /**
+     * For strategies which create a builder helper class that creates the 
instance, the method name to call to create the instance.
+     * Default is determined by the strategy, e.g. <em>build</em> or 
<em>create</em>.
+     */
+    String buildMethodName() default Undefined.STRING;
+
+    /**
+     * The method name to use for a builder factory method in the source class 
for easy access of the
+     * builder helper class for strategies which create such a helper class.
+     * Must not be used if using {@code forClass}.
+     * Default is determined by the strategy, e.g. <em>builder</em> or 
<em>createInitializer</em>.
+     */
+    String builderMethodName() default Undefined.STRING;
+
+    /**
+     * List of field and/or property names to exclude from generated builder 
methods.
+     * Must not be used if 'includes' is used. For convenience, a String with 
comma separated names
+     * can be used in addition to an array (using Groovy's literal list 
notation) of String values.
+     */
+    String[] excludes() default {};
+
+    /**
+     * List of field and/or property names to include within the generated 
builder methods.
+     * Must not be used if 'excludes' is used. For convenience, a String with 
comma separated names
+     * can be used in addition to an array (using Groovy's literal list 
notation) of String values.
+     * The default value is a special marker value indicating that no includes 
are defined; all fields
+     * are included if includes remains undefined and excludes is explicitly 
or implicitly an empty list.
+     */
+    String[] includes() default {Undefined.STRING};
+
+    /**
+     * By default, properties are set directly using their respective field.
+     * By setting {@code useSetters=true} then a writable property will be set 
using its setter.
+     * If turning on this flag we recommend that setters that might be called 
are
+     * made null-safe wrt the parameter.
+     */
+    boolean useSetters() default false;
+
+    /**
+     * Generate builder methods for properties from super classes.
+     */
+    boolean includeSuperProperties() default false;
+
+    /**
+     * Whether the generated builder should support all properties, including 
those with names that are considered internal.
+     *
+     * @since 2.5.0
+     */
+    boolean allNames() default false;
+
+    /**
+     * Whether to include all properties (as per the JavaBean spec) in the 
generated builder.
+     * Groovy recognizes any field-like definitions with no explicit 
visibility as property definitions
+     * and always includes them in the {@code @Builder} generated classes. 
Groovy also treats any explicitly created getXxx() or isYyy()
+     * methods as property getters as per the JavaBean specification. Old 
versions of Groovy did not.
+     * So set this flag to false for the old behavior or if you want to 
explicitly exclude such properties.
+     * Currently only supported by DefaultStrategy and ExternalStrategy.
+     *
+     * @since 2.5.0
+     */
+    boolean allProperties() default true;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/DefaultStrategy.java 
b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
new file mode 100644
index 0000000..65d90e3
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/DefaultStrategy.java
@@ -0,0 +1,293 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.transform.BuilderASTTransformation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGenerics;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.newClass;
+import static 
org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
+import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_PARAMS;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+
+/**
+ * This strategy is used with the {@link Builder} AST transform to create a 
builder helper class
+ * for the fluent creation of instances of a specified class.&nbsp;It can be 
used at the class,
+ * static method or constructor levels.
+ *
+ * You use it as follows:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.builder.*
+ *
+ * {@code @Builder}
+ * class Person {
+ *     String firstName
+ *     String lastName
+ *     int age
+ * }
+ * def person = 
Person.builder().firstName("Robert").lastName("Lewandowski").age(21).build()
+ * assert person.firstName == "Robert"
+ * assert person.lastName == "Lewandowski"
+ * assert person.age == 21
+ * </pre>
+ * The {@code prefix} annotation attribute can be used to create setters with 
a different naming convention. The default is the
+ * empty string but you could change that to "set" as follows:
+ * <pre class="groovyTestCase">
+ * {@code @groovy.transform.builder.Builder}(prefix='set')
+ * class Person {
+ *     String firstName
+ *     String lastName
+ *     int age
+ * }
+ * def p2 = 
Person.builder().setFirstName("Robert").setLastName("Lewandowski").setAge(21).build()
+ * </pre>
+ * or using a prefix of 'with' would result in usage like this:
+ * <pre>
+ * def p3 = 
Person.builder().withFirstName("Robert").withLastName("Lewandowski").withAge(21).build()
+ * </pre>
+ *
+ * You can also use the {@code @Builder} annotation in combination with this 
strategy on one or more constructor or
+ * static method instead of or in addition to using it at the class level. An 
example with a constructor follows:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.ToString
+ * import groovy.transform.builder.Builder
+ *
+ * {@code @ToString}
+ * class Person {
+ *     String first, last
+ *     int born
+ *
+ *     {@code @Builder}
+ *     Person(String roleName) {
+ *         if (roleName == 'Jack Sparrow') {
+ *             first = 'Johnny'; last = 'Depp'; born = 1963
+ *         }
+ *     }
+ * }
+ * assert Person.builder().roleName("Jack Sparrow").build().toString() == 
'Person(Johnny, Depp, 1963)'
+ * </pre>
+ * In this case, the parameter(s) for the constructor or static method become 
the properties available
+ * in the builder. For the case of a static method, the return type of the 
static method becomes the
+ * class of the instance being created. For static factory methods, this is 
normally the class containing the
+ * static method but in general it can be any class.
+ *
+ * Note: if using more than one {@code @Builder} annotation, which is only 
possible when using static method
+ * or constructor variants, it is up to you to ensure that any generated 
helper classes or builder methods
+ * have unique names. E.g.&nbsp;we can modify the previous example to have 
three builders. At least two of the builders
+ * in our case will need to set the 'builderClassName' and 'builderMethodName' 
annotation attributes to ensure
+ * we have unique names. This is shown in the following example:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.builder.*
+ * import groovy.transform.*
+ *
+ * {@code @ToString}
+ * {@code @Builder}
+ * class Person {
+ *     String first, last
+ *     int born
+ *
+ *     Person(){} // required to retain no-arg constructor
+ *
+ *     {@code @Builder}(builderClassName='MovieBuilder', 
builderMethodName='byRoleBuilder')
+ *     Person(String roleName) {
+ *         if (roleName == 'Jack Sparrow') {
+ *             this.first = 'Johnny'; this.last = 'Depp'; this.born = 1963
+ *         }
+ *     }
+ *
+ *     {@code @Builder}(builderClassName='SplitBuilder', 
builderMethodName='splitBuilder')
+ *     static Person split(String name, int year) {
+ *         def parts = name.split(' ')
+ *         new Person(first: parts[0], last: parts[1], born: year)
+ *     }
+ * }
+ *
+ * assert Person.splitBuilder().name("Johnny 
Depp").year(1963).build().toString() == 'Person(Johnny, Depp, 1963)'
+ * assert Person.byRoleBuilder().roleName("Jack Sparrow").build().toString() 
== 'Person(Johnny, Depp, 1963)'
+ * assert 
Person.builder().first("Johnny").last('Depp').born(1963).build().toString() == 
'Person(Johnny, Depp, 1963)'
+ * </pre>
+ *
+ * The 'forClass' annotation attribute for the {@code @Builder} transform 
isn't applicable for this strategy.
+ * The 'useSetters' annotation attribute for the {@code @Builder} transform is 
ignored by this strategy which always uses setters.
+ */
+public class DefaultStrategy extends 
BuilderASTTransformation.AbstractBuilderStrategy {
+    private static final Expression DEFAULT_INITIAL_VALUE = null;
+    private static final int PUBLIC_STATIC = ACC_PUBLIC | ACC_STATIC;
+
+    public void build(BuilderASTTransformation transform, AnnotatedNode 
annotatedNode, AnnotationNode anno) {
+        if (unsupportedAttribute(transform, anno, "forClass")) return;
+        if (annotatedNode instanceof ClassNode) {
+            buildClass(transform, (ClassNode) annotatedNode, anno);
+        } else if (annotatedNode instanceof MethodNode) {
+            buildMethod(transform, (MethodNode) annotatedNode, anno);
+        }
+    }
+
+    public void buildMethod(BuilderASTTransformation transform, MethodNode 
mNode, AnnotationNode anno) {
+        if (transform.getMemberValue(anno, "includes") != null || 
transform.getMemberValue(anno, "excludes") != null) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME +
+                    " processing: includes/excludes only allowed on classes", 
anno);
+        }
+        ClassNode buildee = mNode.getDeclaringClass();
+        ClassNode builder = createBuilder(anno, buildee);
+        createBuilderFactoryMethod(anno, buildee, builder);
+        for (Parameter parameter : mNode.getParameters()) {
+            builder.addField(createFieldCopy(buildee, parameter));
+            builder.addMethod(createBuilderMethodForProp(builder, new 
PropertyInfo(parameter.getName(), parameter.getType()), getPrefix(anno)));
+        }
+        builder.addMethod(createBuildMethodForMethod(anno, buildee, mNode, 
mNode.getParameters()));
+    }
+
+    public void buildClass(BuilderASTTransformation transform, ClassNode 
buildee, AnnotationNode anno) {
+        List<String> excludes = new ArrayList<String>();
+        List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
+        if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) 
return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) 
includes = null;
+        ClassNode builder = createBuilder(anno, buildee);
+        createBuilderFactoryMethod(anno, buildee, builder);
+//        List<FieldNode> fields = getFields(transform, anno, buildee);
+        boolean allNames = transform.memberHasValue(anno, "allNames", true);
+        boolean allProperties = !transform.memberHasValue(anno, 
"allProperties", false);
+        List<PropertyInfo> props = getPropertyInfos(transform, anno, buildee, 
excludes, includes, allNames, allProperties);
+        for (PropertyInfo pi : props) {
+            ClassNode correctedType = getCorrectedType(buildee, pi.getType(), 
builder);
+            String fieldName = pi.getName();
+            builder.addField(createFieldCopy(buildee, fieldName, 
correctedType));
+            builder.addMethod(createBuilderMethodForProp(builder, new 
PropertyInfo(fieldName, correctedType), getPrefix(anno)));
+        }
+        builder.addMethod(createBuildMethod(anno, buildee, props));
+    }
+
+    private static ClassNode getCorrectedType(ClassNode buildee, ClassNode 
fieldType, ClassNode declaringClass) {
+        Map<String,ClassNode> genericsSpec = 
createGenericsSpec(declaringClass);
+        extractSuperClassGenerics(fieldType, buildee, genericsSpec);
+        return correctToGenericsSpecRecurse(genericsSpec, fieldType);
+    }
+
+    private static void createBuilderFactoryMethod(AnnotationNode anno, 
ClassNode buildee, ClassNode builder) {
+        buildee.getModule().addClass(builder);
+        buildee.addMethod(createBuilderMethod(anno, builder));
+    }
+
+    private static ClassNode createBuilder(AnnotationNode anno, ClassNode 
buildee) {
+        return new InnerClassNode(buildee, getFullName(anno, buildee), 
PUBLIC_STATIC, OBJECT_TYPE);
+    }
+
+    private static String getFullName(AnnotationNode anno, ClassNode buildee) {
+        String builderClassName = getMemberStringValue(anno, 
"builderClassName", buildee.getNameWithoutPackage() + "Builder");
+        return buildee.getName() + "$" + builderClassName;
+    }
+
+    private static String getPrefix(AnnotationNode anno) {
+        return getMemberStringValue(anno, "prefix", "");
+    }
+
+    private static MethodNode createBuildMethodForMethod(AnnotationNode anno, 
ClassNode buildee, MethodNode mNode, Parameter[] params) {
+        String buildMethodName = getMemberStringValue(anno, "buildMethodName", 
"build");
+        final BlockStatement body = new BlockStatement();
+        ClassNode returnType;
+        if (mNode instanceof ConstructorNode) {
+            returnType = newClass(buildee);
+            
body.addStatement(returnS(ctorX(newClass(mNode.getDeclaringClass()), 
args(params))));
+        } else {
+            
body.addStatement(returnS(callX(newClass(mNode.getDeclaringClass()), 
mNode.getName(), args(params))));
+            returnType = newClass(mNode.getReturnType());
+        }
+        return new MethodNode(buildMethodName, ACC_PUBLIC, returnType, 
NO_PARAMS, NO_EXCEPTIONS, body);
+    }
+
+    private static MethodNode createBuilderMethod(AnnotationNode anno, 
ClassNode builder) {
+        String builderMethodName = getMemberStringValue(anno, 
"builderMethodName", "builder");
+        final BlockStatement body = new BlockStatement();
+        body.addStatement(returnS(ctorX(builder)));
+        return new MethodNode(builderMethodName, PUBLIC_STATIC, builder, 
NO_PARAMS, NO_EXCEPTIONS, body);
+    }
+
+    private static MethodNode createBuildMethod(AnnotationNode anno, ClassNode 
buildee, List<PropertyInfo> props) {
+        String buildMethodName = getMemberStringValue(anno, "buildMethodName", 
"build");
+        final BlockStatement body = new BlockStatement();
+        body.addStatement(returnS(initializeInstance(buildee, props, body)));
+        return new MethodNode(buildMethodName, ACC_PUBLIC, newClass(buildee), 
NO_PARAMS, NO_EXCEPTIONS, body);
+    }
+
+    private MethodNode createBuilderMethodForProp(ClassNode builder, 
PropertyInfo pinfo, String prefix) {
+        ClassNode fieldType = pinfo.getType();
+        String fieldName = pinfo.getName();
+        String setterName = getSetterName(prefix, fieldName);
+        return new MethodNode(setterName, ACC_PUBLIC, newClass(builder), 
params(param(fieldType, fieldName)), NO_EXCEPTIONS, block(
+                stmt(assignX(propX(varX("this"), constX(fieldName)), 
varX(fieldName, fieldType))),
+                returnS(varX("this", builder))
+        ));
+    }
+
+    private static FieldNode createFieldCopy(ClassNode buildee, Parameter 
param) {
+        Map<String,ClassNode> genericsSpec = createGenericsSpec(buildee);
+        extractSuperClassGenerics(param.getType(), buildee, genericsSpec);
+        ClassNode correctedParamType = 
correctToGenericsSpecRecurse(genericsSpec, param.getType());
+        return new FieldNode(param.getName(), ACC_PRIVATE, correctedParamType, 
buildee, param.getInitialExpression());
+    }
+
+    private static FieldNode createFieldCopy(ClassNode buildee, String 
fieldName, ClassNode fieldType) {
+        return new FieldNode(fieldName, ACC_PRIVATE, fieldType, buildee, 
DEFAULT_INITIAL_VALUE);
+    }
+
+    private static Expression initializeInstance(ClassNode buildee, 
List<PropertyInfo> props, BlockStatement body) {
+        Expression instance = varX("_the" + buildee.getNameWithoutPackage(), 
buildee);
+        body.addStatement(declS(instance, ctorX(buildee)));
+        for (PropertyInfo pi : props) {
+            body.addStatement(stmt(assignX(propX(instance, pi.getName()), 
varX(pi.getName(), pi.getType()))));
+        }
+        return instance;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/ExternalStrategy.java 
b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
new file mode 100644
index 0000000..c482bef
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/ExternalStrategy.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.transform.BuilderASTTransformation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.newClass;
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.MY_TYPE_NAME;
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
+import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_PARAMS;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
+/**
+ * This strategy is used with the {@link Builder} AST transform to populate a 
builder helper class
+ * so that it can be used for the fluent creation of instances of a specified 
class.&nbsp;The specified class is not modified in any way and may be a Java 
class.
+ *
+ * You use it by creating and annotating an explicit builder class which will 
be filled in by during
+ * annotation processing with the appropriate build method and setters. An 
example is shown here:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.builder.*
+ *
+ * class Person {
+ *     String firstName
+ *     String lastName
+ * }
+ *
+ * {@code @Builder}(builderStrategy=ExternalStrategy, forClass=Person)
+ * class PersonBuilder { }
+ *
+ * def person = new 
PersonBuilder().firstName("Robert").lastName("Lewandowski").build()
+ * assert person.firstName == "Robert"
+ * assert person.lastName == "Lewandowski"
+ * </pre>
+ * The {@code prefix} annotation attribute, which defaults to the empty String 
for this strategy, can be used to create setters with a different naming 
convention, e.g. with
+ * the {@code prefix} changed to 'set', you would use your setters as follows:
+ * <pre>
+ * def p1 = new 
PersonBuilder().setFirstName("Robert").setLastName("Lewandowski").setAge(21).build()
+ * </pre>
+ * or using a prefix of 'with':
+ * <pre>
+ * def p2 = new 
PersonBuilder().withFirstName("Robert").withLastName("Lewandowski").withAge(21).build()
+ * </pre>
+ *
+ * The properties to use can be filtered using either the 'includes' or 
'excludes' annotation attributes for {@code @Builder}.
+ * The {@code @Builder} 'buildMethodName' annotation attribute can be used for 
configuring the build method's name, default "build".
+ *
+ * The {@code @Builder} 'builderMethodName' and 'builderClassName' annotation 
attributes aren't applicable for this strategy.
+ * The {@code @Builder} 'useSetters' annotation attribute is ignored by this 
strategy which always uses setters.
+ */
+public class ExternalStrategy extends 
BuilderASTTransformation.AbstractBuilderStrategy {
+    private static final Expression DEFAULT_INITIAL_VALUE = null;
+
+    public void build(BuilderASTTransformation transform, AnnotatedNode 
annotatedNode, AnnotationNode anno) {
+        if (!(annotatedNode instanceof ClassNode)) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME + " processing: building for " +
+                    annotatedNode.getClass().getSimpleName() + " not supported 
by " + getClass().getSimpleName(), annotatedNode);
+            return;
+        }
+        ClassNode builder = (ClassNode) annotatedNode;
+        String prefix = transform.getMemberStringValue(anno, "prefix", "");
+        ClassNode buildee = transform.getMemberClassValue(anno, "forClass");
+        if (buildee == null) {
+            transform.addError("Error during " + MY_TYPE_NAME + " processing: 
'forClass' must be specified for " + getClass().getName(), anno);
+            return;
+        }
+        List<String> excludes = new ArrayList<String>();
+        List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
+        if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) 
return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) 
includes = null;
+        if (unsupportedAttribute(transform, anno, "builderClassName")) return;
+        if (unsupportedAttribute(transform, anno, "builderMethodName")) return;
+        boolean allNames = transform.memberHasValue(anno, "allNames", true);
+        boolean allProperties = !transform.memberHasValue(anno, 
"allProperties", false);
+        List<PropertyInfo> props = getPropertyInfos(transform, anno, buildee, 
excludes, includes, allNames, allProperties);
+        if (includes != null) {
+            for (String name : includes) {
+                checkKnownProperty(transform, anno, name, props);
+            }
+        }
+        for (PropertyInfo prop : props) {
+            builder.addField(createFieldCopy(builder, prop));
+            builder.addMethod(createBuilderMethodForField(builder, prop, 
prefix));
+        }
+        builder.addMethod(createBuildMethod(transform, anno, buildee, props));
+    }
+
+    private static MethodNode createBuildMethod(BuilderASTTransformation 
transform, AnnotationNode anno, ClassNode sourceClass, List<PropertyInfo> 
fields) {
+        String buildMethodName = transform.getMemberStringValue(anno, 
"buildMethodName", "build");
+        final BlockStatement body = new BlockStatement();
+        Expression sourceClassInstance = initializeInstance(sourceClass, 
fields, body);
+        body.addStatement(returnS(sourceClassInstance));
+        return new MethodNode(buildMethodName, ACC_PUBLIC, sourceClass, 
NO_PARAMS, NO_EXCEPTIONS, body);
+    }
+
+    private MethodNode createBuilderMethodForField(ClassNode builderClass, 
PropertyInfo prop, String prefix) {
+        String propName = prop.getName().equals("class") ? "clazz" : 
prop.getName();
+        String setterName = getSetterName(prefix, prop.getName());
+        return new MethodNode(setterName, ACC_PUBLIC, newClass(builderClass), 
params(param(newClass(prop.getType()), propName)), NO_EXCEPTIONS, block(
+                stmt(assignX(propX(varX("this"), constX(propName)), 
varX(propName))),
+                returnS(varX("this", newClass(builderClass)))
+        ));
+    }
+
+    private static FieldNode createFieldCopy(ClassNode builderClass, 
PropertyInfo prop) {
+        String propName = prop.getName();
+        return new FieldNode(propName.equals("class") ? "clazz" : propName, 
ACC_PRIVATE, newClass(prop.getType()), builderClass, DEFAULT_INITIAL_VALUE);
+    }
+
+    private static Expression initializeInstance(ClassNode sourceClass, 
List<PropertyInfo> props, BlockStatement body) {
+        Expression instance = varX("_the" + 
sourceClass.getNameWithoutPackage(), sourceClass);
+        body.addStatement(declS(instance, ctorX(sourceClass)));
+        for (PropertyInfo prop : props) {
+            body.addStatement(stmt(assignX(propX(instance, prop.getName()), 
varX(prop.getName().equals("class") ? "clazz" : prop.getName(), 
newClass(prop.getType())))));
+        }
+        return instance;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/InitializerStrategy.java 
b/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
new file mode 100644
index 0000000..e59dac5
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/InitializerStrategy.java
@@ -0,0 +1,388 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.BuilderASTTransformation;
+import org.codehaus.groovy.transform.ImmutableASTTransformation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.extractSuperClassGenerics;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.makeClassSafeWithGenerics;
+import static 
org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
+import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_PARAMS;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+
+/**
+ * This strategy is used with the {@link Builder} AST transform to create a 
builder helper class
+ * for the fluent and type-safe creation of instances of a specified class.
+ *
+ * It is modelled roughly on the design outlined here:
+ * http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/
+ *
+ * You define classes which use the type-safe initializer pattern as follows:
+ * <pre>
+ * import groovy.transform.builder.*
+ * import groovy.transform.*
+ *
+ * {@code @ToString}
+ * {@code @Builder}(builderStrategy=InitializerStrategy) class Person {
+ *     String firstName
+ *     String lastName
+ *     int age
+ * }
+ * </pre>
+ * While it isn't required to do so, the benefit of this builder strategy 
comes in conjunction with static type-checking or static compilation. Typical 
usage is as follows:
+ * <pre>
+ * {@code @CompileStatic}
+ * def main() {
+ *     println new 
Person(Person.createInitializer().firstName("John").lastName("Smith").age(21))
+ * }
+ * </pre>
+ * which prints:
+ * <pre>
+ * Person(John, Smith, 21)
+ * </pre>
+ * If you don't initialise some of the properties, your code won't compile, 
e.g. if the method body above was changed to this:
+ * <pre>
+ * println new 
Person(Person.createInitializer().firstName("John").lastName("Smith"))
+ * </pre>
+ * then the following compile-time error would result:
+ * <pre>
+ * [Static type checking] - Cannot find matching method 
Person#<init>(Person$PersonInitializer 
<groovy.transform.builder.InitializerStrategy$SET, 
groovy.transform.builder.InitializerStrategy$SET, 
groovy.transform.builder.InitializerStrategy$UNSET>). Please check if the 
declared type is correct and if the method exists.
+ * </pre>
+ * The message is a little cryptic, but it is basically the static compiler 
telling us that the third parameter, {@code age} in our case, is unset.
+ *
+ * You can also add this annotation to your predefined constructors. These 
will be made private and an initializer will be set up
+ * to call your constructor. Any parameters to your constructor become the 
properties expected by the initializer.
+ * If you use such a builder on a constructor as well as on the class or on 
more than one constructor, then it is up to you
+ * to define unique values for 'builderClassName' and 'builderMethodName' for 
each annotation.
+ */
+public class InitializerStrategy extends 
BuilderASTTransformation.AbstractBuilderStrategy {
+
+    /**
+     * Internal phantom type used by the {@code InitializerStrategy} to 
indicate that a property has been set. It is used in conjunction with the 
generated parameterized type helper class.
+     */
+    public abstract static class SET {
+    }
+
+    /**
+     * Internal phantom type used by the {@code InitializerStrategy} to 
indicate that a property remains unset. It is used in conjunction with the 
generated parameterized type helper class.
+     */
+    public abstract static class UNSET {
+    }
+
+    private static final int PUBLIC_STATIC = ACC_PUBLIC | ACC_STATIC;
+    private static final Expression DEFAULT_INITIAL_VALUE = null;
+
+    public void build(BuilderASTTransformation transform, AnnotatedNode 
annotatedNode, AnnotationNode anno) {
+        if (unsupportedAttribute(transform, anno, "forClass")) return;
+        if (unsupportedAttribute(transform, anno, "allProperties")) return;
+        boolean useSetters = transform.memberHasValue(anno, "useSetters", 
true);
+        boolean allNames = transform.memberHasValue(anno, "allNames", true);
+        if (annotatedNode instanceof ClassNode) {
+            createBuilderForAnnotatedClass(transform, (ClassNode) 
annotatedNode, anno, useSetters, allNames);
+        } else if (annotatedNode instanceof MethodNode) {
+            createBuilderForAnnotatedMethod(transform, (MethodNode) 
annotatedNode, anno, useSetters);
+        }
+    }
+
+    private void createBuilderForAnnotatedClass(BuilderASTTransformation 
transform, ClassNode buildee, AnnotationNode anno, boolean useSetters, boolean 
allNames) {
+        List<String> excludes = new ArrayList<String>();
+        List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
+        if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) 
return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) 
includes = null;
+        List<FieldNode> fields = getFields(transform, anno, buildee);
+        List<FieldNode> filteredFields = filterFields(fields, includes, 
excludes, allNames);
+        if (filteredFields.isEmpty()) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME +
+                    " processing: at least one property is required for this 
strategy", anno);
+        }
+        ClassNode builder = createInnerHelperClass(buildee, 
getBuilderClassName(buildee, anno), filteredFields.size());
+        addFields(buildee, filteredFields, builder);
+
+        buildCommon(buildee, anno, filteredFields, builder);
+        createBuildeeConstructors(transform, buildee, builder, filteredFields, 
true, useSetters);
+    }
+
+    private void createBuilderForAnnotatedMethod(BuilderASTTransformation 
transform, MethodNode mNode, AnnotationNode anno, boolean useSetters) {
+        if (transform.getMemberValue(anno, "includes") != null || 
transform.getMemberValue(anno, "excludes") != null) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME +
+                    " processing: includes/excludes only allowed on classes", 
anno);
+        }
+        if (mNode instanceof ConstructorNode) {
+            mNode.setModifiers(ACC_PRIVATE | ACC_SYNTHETIC);
+        } else {
+            if ((mNode.getModifiers() & ACC_STATIC) == 0) {
+                transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME +
+                        " processing: method builders only allowed on static 
methods", anno);
+            }
+            mNode.setModifiers(ACC_PRIVATE | ACC_SYNTHETIC | ACC_STATIC);
+        }
+        ClassNode buildee = mNode.getDeclaringClass();
+        Parameter[] parameters = mNode.getParameters();
+        if (parameters.length == 0) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME +
+                    " processing: at least one parameter is required for this 
strategy", anno);
+        }
+        ClassNode builder = createInnerHelperClass(buildee, 
getBuilderClassName(buildee, anno), parameters.length);
+        List<FieldNode> convertedFields = convertParamsToFields(builder, 
parameters);
+
+        buildCommon(buildee, anno, convertedFields, builder);
+        if (mNode instanceof ConstructorNode) {
+            createBuildeeConstructors(transform, buildee, builder, 
convertedFields, false, useSetters);
+        } else {
+            createBuildeeMethods(buildee, mNode, builder, convertedFields);
+        }
+    }
+
+    private static String getBuilderClassName(ClassNode buildee, 
AnnotationNode anno) {
+        return getMemberStringValue(anno, "builderClassName", 
buildee.getNameWithoutPackage() + "Initializer");
+    }
+
+    private static void addFields(ClassNode buildee, List<FieldNode> 
filteredFields, ClassNode builder) {
+        for (FieldNode filteredField : filteredFields) {
+            builder.addField(createFieldCopy(buildee, filteredField));
+        }
+    }
+
+    private void buildCommon(ClassNode buildee, AnnotationNode anno, 
List<FieldNode> fieldNodes, ClassNode builder) {
+        String prefix = getMemberStringValue(anno, "prefix", "");
+        String buildMethodName = getMemberStringValue(anno, "buildMethodName", 
"create");
+        createBuilderConstructors(builder, buildee, fieldNodes);
+        buildee.getModule().addClass(builder);
+        String builderMethodName = getMemberStringValue(anno, 
"builderMethodName", "createInitializer");
+        buildee.addMethod(createBuilderMethod(buildMethodName, builder, 
fieldNodes.size(), builderMethodName));
+        for (int i = 0; i < fieldNodes.size(); i++) {
+            builder.addMethod(createBuilderMethodForField(builder, fieldNodes, 
prefix, i));
+        }
+        builder.addMethod(createBuildMethod(builder, buildMethodName, 
fieldNodes));
+    }
+
+    private static List<FieldNode> convertParamsToFields(ClassNode builder, 
Parameter[] parameters) {
+        List<FieldNode> fieldNodes = new ArrayList<FieldNode>();
+        for(Parameter parameter: parameters) {
+            Map<String,ClassNode> genericsSpec = createGenericsSpec(builder);
+            ClassNode correctedType = 
correctToGenericsSpecRecurse(genericsSpec, parameter.getType());
+            FieldNode fieldNode = new FieldNode(parameter.getName(), 
parameter.getModifiers(), correctedType, builder, DEFAULT_INITIAL_VALUE);
+            fieldNodes.add(fieldNode);
+            builder.addField(fieldNode);
+        }
+        return fieldNodes;
+    }
+
+    private static ClassNode createInnerHelperClass(ClassNode buildee, String 
builderClassName, int fieldsSize) {
+        final String fullName = buildee.getName() + "$" + builderClassName;
+        ClassNode builder = new InnerClassNode(buildee, fullName, 
PUBLIC_STATIC, OBJECT_TYPE);
+        GenericsType[] gtypes = new GenericsType[fieldsSize];
+        for (int i = 0; i < gtypes.length; i++) {
+            gtypes[i] = makePlaceholder(i);
+        }
+        builder.setGenericsTypes(gtypes);
+        return builder;
+    }
+
+    private static MethodNode createBuilderMethod(String buildMethodName, 
ClassNode builder, int numFields, String builderMethodName) {
+        final BlockStatement body = new BlockStatement();
+        body.addStatement(returnS(callX(builder, buildMethodName)));
+        ClassNode returnType = makeClassSafeWithGenerics(builder, 
unsetGenTypes(numFields));
+        return new MethodNode(builderMethodName, PUBLIC_STATIC, returnType, 
NO_PARAMS, NO_EXCEPTIONS, body);
+    }
+
+    private static GenericsType[] unsetGenTypes(int numFields) {
+        GenericsType[] gtypes = new GenericsType[numFields];
+        for (int i = 0; i < gtypes.length; i++) {
+            gtypes[i] = new GenericsType(ClassHelper.make(UNSET.class));
+        }
+        return gtypes;
+    }
+
+    private static GenericsType[] setGenTypes(int numFields) {
+        GenericsType[] gtypes = new GenericsType[numFields];
+        for (int i = 0; i < gtypes.length; i++) {
+            gtypes[i] = new GenericsType(ClassHelper.make(SET.class));
+        }
+        return gtypes;
+    }
+
+    private static void createBuilderConstructors(ClassNode builder, ClassNode 
buildee, List<FieldNode> fields) {
+        builder.addConstructor(ACC_PRIVATE, NO_PARAMS, NO_EXCEPTIONS, 
block(ctorSuperS()));
+        final BlockStatement body = new BlockStatement();
+        body.addStatement(ctorSuperS());
+        initializeFields(fields, body, false);
+        builder.addConstructor(ACC_PRIVATE, getParams(fields, buildee), 
NO_EXCEPTIONS, body);
+    }
+
+    private static void createBuildeeConstructors(BuilderASTTransformation 
transform, ClassNode buildee, ClassNode builder, List<FieldNode> fields, 
boolean needsConstructor, boolean useSetters) {
+        ConstructorNode initializer = createInitializerConstructor(buildee, 
builder, fields);
+        if (transform.hasAnnotation(buildee, 
ImmutableASTTransformation.MY_TYPE)) {
+            
initializer.putNodeMetaData(ImmutableASTTransformation.IMMUTABLE_SAFE_FLAG, 
Boolean.TRUE);
+        } else if (needsConstructor) {
+            final BlockStatement body = new BlockStatement();
+            body.addStatement(ctorSuperS());
+            initializeFields(fields, body, useSetters);
+            buildee.addConstructor(ACC_PRIVATE | ACC_SYNTHETIC, 
getParams(fields, buildee), NO_EXCEPTIONS, body);
+        }
+    }
+
+    private static void createBuildeeMethods(ClassNode buildee, MethodNode 
mNode, ClassNode builder, List<FieldNode> fields) {
+        ClassNode paramType = makeClassSafeWithGenerics(builder, 
setGenTypes(fields.size()));
+        List<Expression> argsList = new ArrayList<Expression>();
+        Parameter initParam = param(paramType, "initializer");
+        for (FieldNode fieldNode : fields) {
+            argsList.add(propX(varX(initParam), fieldNode.getName()));
+        }
+        String newName = "$" + mNode.getName(); // can't have private and 
public methods of the same name, so rename original
+        buildee.addMethod(mNode.getName(), PUBLIC_STATIC, 
mNode.getReturnType(), params(param(paramType, "initializer")), NO_EXCEPTIONS,
+                block(stmt(callX(buildee, newName, args(argsList)))));
+        renameMethod(buildee, mNode, newName);
+    }
+
+    // no rename so delete and add
+    private static void renameMethod(ClassNode buildee, MethodNode mNode, 
String newName) {
+        buildee.addMethod(newName, mNode.getModifiers(), 
mNode.getReturnType(), mNode.getParameters(), mNode.getExceptions(), 
mNode.getCode());
+        buildee.removeMethod(mNode);
+    }
+
+    private static Parameter[] getParams(List<FieldNode> fields, ClassNode 
cNode) {
+        Parameter[] parameters = new Parameter[fields.size()];
+        for (int i = 0; i < parameters.length; i++) {
+            FieldNode fNode = fields.get(i);
+            Map<String,ClassNode> genericsSpec = 
createGenericsSpec(fNode.getDeclaringClass());
+            extractSuperClassGenerics(fNode.getType(), cNode, genericsSpec);
+            ClassNode correctedType = 
correctToGenericsSpecRecurse(genericsSpec, fNode.getType());
+            parameters[i] = new Parameter(correctedType, fNode.getName());
+        }
+        return parameters;
+    }
+
+    private static ConstructorNode createInitializerConstructor(ClassNode 
buildee, ClassNode builder, List<FieldNode> fields) {
+        ClassNode paramType = makeClassSafeWithGenerics(builder, 
setGenTypes(fields.size()));
+        List<Expression> argsList = new ArrayList<Expression>();
+        Parameter initParam = param(paramType, "initializer");
+        for (FieldNode fieldNode : fields) {
+            argsList.add(propX(varX(initParam), fieldNode.getName()));
+        }
+        return buildee.addConstructor(ACC_PUBLIC, params(param(paramType, 
"initializer")), NO_EXCEPTIONS, block(ctorThisS(args(argsList))));
+    }
+
+    private static MethodNode createBuildMethod(ClassNode builder, String 
buildMethodName, List<FieldNode> fields) {
+        ClassNode returnType = makeClassSafeWithGenerics(builder, 
unsetGenTypes(fields.size()));
+        return new MethodNode(buildMethodName, PUBLIC_STATIC, returnType, 
NO_PARAMS, NO_EXCEPTIONS, block(returnS(ctorX(returnType))));
+    }
+
+    private MethodNode createBuilderMethodForField(ClassNode builder, 
List<FieldNode> fields, String prefix, int fieldPos) {
+        String fieldName = fields.get(fieldPos).getName();
+        String setterName = getSetterName(prefix, fieldName);
+        GenericsType[] gtypes = new GenericsType[fields.size()];
+        List<Expression> argList = new ArrayList<Expression>();
+        for (int i = 0; i < fields.size(); i++) {
+            gtypes[i] = i == fieldPos ? new 
GenericsType(ClassHelper.make(SET.class)) : makePlaceholder(i);
+            argList.add(i == fieldPos ? propX(varX("this"), constX(fieldName)) 
: varX(fields.get(i).getName()));
+        }
+        ClassNode returnType = makeClassSafeWithGenerics(builder, gtypes);
+        FieldNode fNode = fields.get(fieldPos);
+        Map<String,ClassNode> genericsSpec = 
createGenericsSpec(fNode.getDeclaringClass());
+        extractSuperClassGenerics(fNode.getType(), builder, genericsSpec);
+        ClassNode correctedType = correctToGenericsSpecRecurse(genericsSpec, 
fNode.getType());
+        return new MethodNode(setterName, ACC_PUBLIC, returnType, 
params(param(correctedType, fieldName)), NO_EXCEPTIONS, block(
+                stmt(assignX(propX(varX("this"), constX(fieldName)), 
varX(fieldName, correctedType))),
+                returnS(ctorX(returnType, args(argList)))
+        ));
+    }
+
+    private static GenericsType makePlaceholder(int i) {
+        ClassNode type = ClassHelper.makeWithoutCaching("T" + i);
+        type.setRedirect(OBJECT_TYPE);
+        type.setGenericsPlaceHolder(true);
+        return new GenericsType(type);
+    }
+
+    private static FieldNode createFieldCopy(ClassNode buildee, FieldNode 
fNode) {
+        Map<String,ClassNode> genericsSpec = 
createGenericsSpec(fNode.getDeclaringClass());
+        extractSuperClassGenerics(fNode.getType(), buildee, genericsSpec);
+        ClassNode correctedType = correctToGenericsSpecRecurse(genericsSpec, 
fNode.getType());
+        return new FieldNode(fNode.getName(), fNode.getModifiers(), 
correctedType, buildee, DEFAULT_INITIAL_VALUE);
+    }
+
+    private static List<FieldNode> filterFields(List<FieldNode> fieldNodes, 
List<String> includes, List<String> excludes, boolean allNames) {
+        List<FieldNode> fields = new ArrayList<FieldNode>();
+        for (FieldNode fNode : fieldNodes) {
+            if 
(AbstractASTTransformation.shouldSkipUndefinedAware(fNode.getName(), excludes, 
includes, allNames)) continue;
+            fields.add(fNode);
+        }
+        return fields;
+    }
+
+    private static void initializeFields(List<FieldNode> fields, 
BlockStatement body, boolean useSetters) {
+        for (FieldNode field : fields) {
+            String name = field.getName();
+            body.addStatement(
+                    stmt(useSetters && !field.isFinal()
+                                    ? callThisX(getSetterName(name), 
varX(param(field.getType(), name)))
+                                    : assignX(propX(varX("this"), 
field.getName()), varX(param(field.getType(), name)))
+                    )
+            );
+        }
+    }
+
+    private static String getSetterName(String name) {
+        return "set" + Verifier.capitalize(name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/SimpleStrategy.java 
b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
new file mode 100644
index 0000000..7956ac6
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
@@ -0,0 +1,132 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.BuilderASTTransformation;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static 
org.codehaus.groovy.ast.tools.GeneralUtils.getInstancePropertyFields;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.newClass;
+import static 
org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
+import static 
org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
+
+/**
+ * This strategy is used with the {@link Builder} AST transform to modify your 
Groovy objects so that the
+ * setter methods for properties return the original object, thus allowing 
chained usage of the setters.
+ *
+ * You use it as follows:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.builder.*
+ *
+ * {@code @Builder}(builderStrategy=SimpleStrategy)
+ * class Person {
+ *     String firstName
+ *     String lastName
+ *     int age
+ * }
+ * def person = new 
Person().setFirstName("Robert").setLastName("Lewandowski").setAge(21)
+ * assert person.firstName == "Robert"
+ * assert person.lastName == "Lewandowski"
+ * assert person.age == 21
+ * </pre>
+ * The {@code prefix} annotation attribute can be used to create setters with 
a different naming convention, e.g. with the prefix set to the empty String, 
you would use your setters as follows:
+ * <pre>
+ * def p1 = new Person().firstName("Robert").lastName("Lewandowski").age(21)
+ * </pre>
+ * or using a prefix of 'with':
+ * <pre>
+ * def p2 = new 
Person().withFirstName("Robert").withLastName("Lewandowski").withAge(21)
+ * </pre>
+ * When using the default prefix of "set", Groovy's normal setters will be 
replaced by the chained versions. When using
+ * a custom prefix, Groovy's unchained setters will still be available for use 
in the normal unchained fashion.
+ *
+ * The 'useSetters' annotation attribute can be used for writable properties 
as per the {@code Builder} transform documentation.
+ * The other annotation attributes for the {@code @Builder} transform for 
configuring the building process aren't applicable for this strategy.
+ *
+ * @author Paul King
+ */
+public class SimpleStrategy extends 
BuilderASTTransformation.AbstractBuilderStrategy {
+    public void build(BuilderASTTransformation transform, AnnotatedNode 
annotatedNode, AnnotationNode anno) {
+        if (!(annotatedNode instanceof ClassNode)) {
+            transform.addError("Error during " + 
BuilderASTTransformation.MY_TYPE_NAME + " processing: building for " +
+                    annotatedNode.getClass().getSimpleName() + " not supported 
by " + getClass().getSimpleName(), annotatedNode);
+            return;
+        }
+        ClassNode buildee = (ClassNode) annotatedNode;
+        if (unsupportedAttribute(transform, anno, "builderClassName")) return;
+        if (unsupportedAttribute(transform, anno, "buildMethodName")) return;
+        if (unsupportedAttribute(transform, anno, "builderMethodName")) return;
+        if (unsupportedAttribute(transform, anno, "forClass")) return;
+        if (unsupportedAttribute(transform, anno, "includeSuperProperties")) 
return;
+        if (unsupportedAttribute(transform, anno, "allProperties")) return;
+        boolean useSetters = transform.memberHasValue(anno, "useSetters", 
true);
+        boolean allNames = transform.memberHasValue(anno, "allNames", true);
+
+        List<String> excludes = new ArrayList<String>();
+        List<String> includes = new ArrayList<String>();
+        includes.add(Undefined.STRING);
+        if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) 
return;
+        if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) 
includes = null;
+        String prefix = getMemberStringValue(anno, "prefix", "set");
+        List<FieldNode> fields = getFields(transform, anno, buildee);
+        if (includes != null) {
+            for (String name : includes) {
+                checkKnownField(transform, anno, name, fields);
+            }
+        }
+        for (FieldNode field : fields) {
+            String fieldName = field.getName();
+            if (!AbstractASTTransformation.shouldSkipUndefinedAware(fieldName, 
excludes, includes, allNames)) {
+                String methodName = getSetterName(prefix, fieldName);
+                Parameter parameter = param(field.getType(), fieldName);
+                buildee.addMethod(methodName, Opcodes.ACC_PUBLIC, 
newClass(buildee), params(parameter), NO_EXCEPTIONS, block(
+                                stmt(useSetters && !field.isFinal()
+                                                ? 
callThisX(getSetterName("set", fieldName), varX(parameter))
+                                                : assignX(fieldX(field), 
varX(parameter))
+                                ),
+                                returnS(varX("this")))
+                );
+            }
+        }
+    }
+
+    @Override
+    protected List<FieldNode> getFields(BuilderASTTransformation transform, 
AnnotationNode anno, ClassNode buildee) {
+        return getInstancePropertyFields(buildee);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/stc/ClosureParams.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ClosureParams.java 
b/src/main/groovy/groovy/transform/stc/ClosureParams.java
new file mode 100644
index 0000000..e788d44
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureParams.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.stc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Parameter annotation aimed at helping IDEs or the static type checker to 
infer the
+ * parameter types of a closure. Without this annotation, a method signature 
may look like
+ * this:<p>
+ * <code>public &lt;T,R&gt; List&lt;R&gt; doSomething(List&lt;T&gt; source, 
Closure&lt;R&gt; consumer)</code>
+ * <p>
+ * <p>The problem this annotation tries to solve is to define the expected 
parameter types of the
+ * <i>consumer</i> closure. The generics type defined in 
<code>Closure&lt;R&gt;</code> correspond to the
+ * result type of the closure, but tell nothing about what the closure must 
accept as arguments.</p>
+ * <p></p>
+ * <p>There's no way in Java or Groovy to express the type signature of the 
expected closure call method from
+ * outside the closure itself, so we rely on an annotation here. 
Unfortunately, annotations also have limitations
+ * (like not being able to use generics placeholder as annotation values) that 
prevent us from expressing the
+ * type directly.</p>
+ * <p>Additionally, closures are polymorphic. This means that a single closure 
can be used with different, valid,
+ * parameter signatures. A typical use case can be found when a closure 
accepts either a {@link java.util.Map.Entry}
+ * or a (key,value) pair, like the {@link 
org.codehaus.groovy.runtime.DefaultGroovyMethods#each(java.util.Map, 
groovy.lang.Closure)}
+ * method.</p>
+ * <p>For those reasons, the {@link ClosureParams} annotation takes these 
arguments:
+ * <ul>
+ *     <li>{@link ClosureParams#value()} defines a {@link 
groovy.transform.stc.ClosureSignatureHint} hint class
+ *     that the compiler will use to infer the parameter types</li>
+ *     <li>{@link ClosureParams#conflictResolutionStrategy()} defines a {@link 
groovy.transform.stc.ClosureSignatureConflictResolver} resolver
+ *     class that the compiler will use to potentially reduce ambiguities 
remaining after initial inference calculations</li>
+ *     <li>{@link ClosureParams#options()}, a set of options that are passed 
to the hint when the type is inferred (and also available to the resolver)</li>
+ * </ul>
+ * </p>
+ * <p>As a result, the previous signature can be written like this:</p>
+ * <code>public &lt;T,R&gt; List&lt;R&gt; doSomething(List&lt;T&gt; source, 
@ClosureParams(FirstParam.FirstGenericType.class) Closure&lt;R&gt; 
consumer)</code>
+ * <p>Which uses the {@link FirstParam.FirstGenericType} first generic type of 
the first argument</p> hint to tell that the only expected
+ * argument type corresponds to the type of the first generic argument type of 
the first method parameter.
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ClosureParams {
+    Class<? extends ClosureSignatureHint> value();
+    Class<? extends ClosureSignatureConflictResolver> 
conflictResolutionStrategy() default ClosureSignatureConflictResolver.class;
+    String[] options() default {};
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java
----------------------------------------------------------------------
diff --git 
a/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java 
b/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java
new file mode 100644
index 0000000..d727958
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.List;
+
+/**
+ * If multiple candidate signatures are found after applying type hints,
+ * a conflict resolver can attempt to resolve the ambiguity.
+ *
+ * @since 2.5.0
+ */
+public class ClosureSignatureConflictResolver {
+    /**
+     *
+     * @param candidates the list of signatures as determined after applying 
type hints and performing initial inference calculations
+     * @param receiver the receiver the method is being called on
+     * @param arguments the arguments for the closure
+     * @param closure the closure expression under analysis
+     * @param methodNode the method for which a {@link groovy.lang.Closure} 
parameter was annotated with {@link ClosureParams}
+     * @param sourceUnit the source unit of the file being compiled
+     * @param compilationUnit the compilation unit of the file being compiled
+     * @param options the options, corresponding to the {@link 
ClosureParams#options()} found on the annotation
+     * @return a non-null list of signatures, where a signature corresponds to 
an array of class nodes, each of them matching a parameter. A list with more 
than one element indicates that all ambiguities haven't yet been resolved.
+     */
+    public List<ClassNode[]> resolve(List<ClassNode[]> candidates, ClassNode 
receiver, Expression arguments, ClosureExpression closure,
+                                     MethodNode methodNode, SourceUnit 
sourceUnit, CompilationUnit compilationUnit, String[] options) {
+        // do nothing by default
+        return candidates;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java 
b/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
new file mode 100644
index 0000000..9a77d20
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.List;
+
+/**
+ * <p>A closure signature hint class is always used in conjunction with the 
{@link ClosureParams} annotation. It is
+ * called at compile time (or may be used by IDEs) to infer the types of the 
parameters of a {@link groovy.lang.Closure}.</p>
+
+ * <p>A closure hint class is responsible for generating the list of arguments 
that a closure accepts. Since closures
+ * may accept several signatures, {@link 
#getClosureSignatures(org.codehaus.groovy.ast.MethodNode, 
org.codehaus.groovy.control.SourceUnit, 
org.codehaus.groovy.control.CompilationUnit, String[], 
org.codehaus.groovy.ast.ASTNode)} should
+ * return a list.</p>
+ *
+ * <p>Whenever the type checker encounters a method call that targets a method 
accepting a closure, it will search
+ * for the {@link ClosureParams} annotation on the {@link groovy.lang.Closure} 
argument. If it is found, then it
+ * creates an instance of the hint class and calls the {@link 
#getClosureSignatures(org.codehaus.groovy.ast.MethodNode, 
org.codehaus.groovy.control.SourceUnit, 
org.codehaus.groovy.control.CompilationUnit, String[], 
org.codehaus.groovy.ast.ASTNode)}
+ * method, which will in turn return the list of signatures.</p>
+ *
+ * <p><i>Note that the signature concept here is used only to describe the 
parameter types, not the result type, which
+ * is found in the generic type argument of the {@link groovy.lang.Closure} 
class.</i></p>
+ *
+ * <p>Several predefined hints can be found, which should cover most of the 
use cases.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ *
+ */
+public abstract class ClosureSignatureHint {
+
+    /**
+     * A helper method which will extract the n-th generic type from a class 
node.
+     * @param type the class node from which to pick a generic type
+     * @param gtIndex the index of the generic type to extract
+     * @return the n-th generic type, or {@link 
org.codehaus.groovy.ast.ClassHelper#OBJECT_TYPE} if it doesn't exist.
+     */
+    public static ClassNode pickGenericType(ClassNode type, int gtIndex) {
+        final GenericsType[] genericsTypes = type.getGenericsTypes();
+        if (genericsTypes==null || genericsTypes.length<gtIndex) {
+            return ClassHelper.OBJECT_TYPE;
+        }
+        return genericsTypes[gtIndex].getType();
+    }
+
+    /**
+     * A helper method which will extract the n-th generic type from the n-th 
parameter of a method node.
+     * @param node the method node from which the generic type should be picked
+     * @param parameterIndex the index of the parameter in the method 
parameter list
+     * @param gtIndex the index of the generic type to extract
+     * @return the generic type, or {@link 
org.codehaus.groovy.ast.ClassHelper#OBJECT_TYPE} if it doesn't exist.
+     */
+    public static ClassNode pickGenericType(MethodNode node, int 
parameterIndex, int gtIndex) {
+        final Parameter[] parameters = node.getParameters();
+        final ClassNode type = parameters[parameterIndex].getOriginType();
+        return pickGenericType(type, gtIndex);
+    }
+
+    /**
+     * <p>Subclasses should implement this method, which returns the list of 
accepted closure signatures.</p>
+     *
+     * <p>The compiler will call this method each time, in a source file, a 
method call using a closure
+     * literal is encountered and that the target method has the corresponding 
{@link groovy.lang.Closure} parameter
+     * annotated with {@link groovy.transform.stc.ClosureParams}. So imagine 
the following code needs to be compiled:</p>
+     *
+     * <code>@groovy.transform.TypeChecked
+     * void doSomething() {
+     *     println ['a','b'].collect { it.toUpperCase() }
+     * }</code>
+     *
+     * <p>The <i>collect</i> method accepts a closure, but normally, the type 
checker doesn't have enough type information
+     * in the sole {@link 
org.codehaus.groovy.runtime.DefaultGroovyMethods#collect(java.util.Collection, 
groovy.lang.Closure)} method
+     * signature to infer the type of <i>it</i>. With the annotation, it will 
now try to find an annotation on the closure parameter.
+     * If it finds it, then an instance of the hint class is created and the 
type checker calls it with the following arguments:</p>
+     * <ul>
+     *     <li>the method node corresponding to the target method (here, the 
{@link 
org.codehaus.groovy.runtime.DefaultGroovyMethods#collect(java.util.Collection, 
groovy.lang.Closure)} method</li>
+     *     <li>the (optional) list of options found in the annotation</li>
+     * </ul>
+     *
+     * <p>Now, the hint instance can return the list of expected parameters. 
Here, it would have to say that the collect method accepts
+     * a closure for which the only argument is of the type of the first 
generic type of the first argument.</p>
+     * <p>With that type information, the type checker can now infer that the 
type of <i>it</i> is <i>String</i>, because the first argument (here the 
receiver of the collect method)
+     * is a <i>List&lt;String&gt;</i></p>
+     *
+     * <p></p>
+     *
+     * <p>Subclasses are therefore expected to return the signatures according 
to the available context, which is only the target method and the potential 
options.</p>
+     *
+     *
+     * @param node the method node for which a {@link groovy.lang.Closure} 
parameter was annotated with
+     *             {@link ClosureParams}
+     * @param sourceUnit the source unit of the file being compiled
+     * @param compilationUnit the compilation unit of the file being compiled
+     * @param options the options, corresponding to the {@link 
ClosureParams#options()} found on the annotation  @return a non-null list of 
signature, where a signature corresponds to an array of class nodes, each of 
them matching a parameter.
+     * @param usage the AST node, in the compiled file, which triggered a call 
to this method. Normally only used for logging/error handling
+     */
+    public abstract List<ClassNode[]> getClosureSignatures(MethodNode node, 
SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options, 
ASTNode usage);
+
+    /**
+     * Finds a class node given a string representing the type. Performs a 
lookup in the compilation unit to check if it is done in the same source unit.
+     * @param sourceUnit source unit
+     * @param compilationUnit compilation unit
+     * @param className the name of the class we want to get a {@link 
org.codehaus.groovy.ast.ClassNode} for
+     * @return a ClassNode representing the type
+     */
+    protected ClassNode findClassNode(final SourceUnit sourceUnit, final 
CompilationUnit compilationUnit, final String className) {
+        if (className.endsWith("[]")) {
+            return findClassNode(sourceUnit, compilationUnit, 
className.substring(0, className.length() - 2)).makeArray();
+        }
+        ClassNode cn = compilationUnit.getClassNode(className);
+        if (cn == null) {
+            try {
+                cn = ClassHelper.make(Class.forName(className, false, 
sourceUnit.getClassLoader()));
+            } catch (ClassNotFoundException e) {
+                cn = ClassHelper.make(className);
+            }
+        }
+        return cn;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/stc/FirstParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FirstParam.java 
b/src/main/groovy/groovy/transform/stc/FirstParam.java
new file mode 100644
index 0000000..81eb0e7
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FirstParam.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * <p>A hint used to instruct the type checker to pick the first parameter 
type. For example:</p>
+ * <code>public &lt;T&gt; def doWith(T src, @ClosureParams(FirstParam.class) 
Closure c) { c.call(src); }</code>
+ *
+ * <p>This class has several inner classes that also helps picking generic 
argument types instead of the parameter type.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FirstParam extends PickAnyArgumentHint {
+    public FirstParam() {
+        super(0,-1);
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the first generic 
type of the first parameter type. For example:</p>
+     * <code>void &lt;T&gt; doWithElements(List&lt;T&gt; src, 
@ClosureParams(FirstParam.FirstGenericType.class) Closure c) { src.each { 
c.call(it) } }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class FirstGenericType extends PickAnyArgumentHint {
+        public FirstGenericType() {
+            super(0,0);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the second generic 
type of the first parameter type. For example:</p>
+     * <code>void &lt;T,U&gt; doWithElements(Tuple&lt;T,U&gt; src, 
@ClosureParams(FirstParam.SecondGenericType.class) Closure c) { src.each { 
c.call(it) } }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class SecondGenericType extends PickAnyArgumentHint {
+        public SecondGenericType() {
+            super(0,1);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the third generic 
type of the first parameter type. For example:</p>
+     * <code>void &lt;T,U,V&gt; doWithElements(Triple&lt;T,U,V&gt; src, 
@ClosureParams(FirstParam.ThirdGenericType.class) Closure c) { src.each { 
c.call(it) } }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class ThirdGenericType extends PickAnyArgumentHint {
+        public ThirdGenericType() {
+            super(0,2);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the type of the 
component of the first parameter type, which is therefore
+     * expected to be an array, like in this example:</p>
+     * <code>void &lt;T&gt; doWithArray(T[] array, 
@ClosureParams(FirstParam.Component.class) Closure c) { array.each { 
c.call(it)} }</code>
+     */
+    public static class Component extends FirstParam {
+        @Override
+        public ClassNode[] getParameterTypes(final MethodNode node, final 
String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, 
final ASTNode usage) {
+            final ClassNode[] parameterTypes = super.getParameterTypes(node, 
options, sourceUnit, unit, usage);
+            parameterTypes[0] = parameterTypes[0].getComponentType();
+            return parameterTypes;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java 
b/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java
new file mode 100644
index 0000000..e5125f1
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.trait.Traits;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This signature hint uses abstract methods from some type (abstract class or 
interface) in order
+ * to infer the expected parameter types. This is especially useful for 
closure parameter type
+ * inference when implicit closure coercion is in action.
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FromAbstractTypeMethods extends ClosureSignatureHint {
+    @Override
+    public List<ClassNode[]> getClosureSignatures(final MethodNode node, final 
SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] 
options, final ASTNode usage) {
+        String className = options[0];
+        ClassNode cn = findClassNode(sourceUnit, compilationUnit, className);
+        return extractSignaturesFromMethods(cn);
+    }
+
+    private static List<ClassNode[]> extractSignaturesFromMethods(final 
ClassNode cn) {
+        List<MethodNode> methods = cn.getAllDeclaredMethods();
+        List<ClassNode[]> signatures = new LinkedList<ClassNode[]>();
+        for (MethodNode method : methods) {
+            if (!method.isSynthetic() && method.isAbstract()) {
+                extractParametersFromMethod(signatures, method);
+            }
+        }
+        return signatures;
+    }
+
+    private static void extractParametersFromMethod(final List<ClassNode[]> 
signatures, final MethodNode method) {
+        if (Traits.hasDefaultImplementation(method)) return;
+        Parameter[] parameters = method.getParameters();
+        ClassNode[] typeParams = new ClassNode[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            typeParams[i] = parameters[i].getOriginType();
+        }
+        signatures.add(typeParams);
+    }
+}

Reply via email to