http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/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..c678c38
--- /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 right 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/d638ca43/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/d638ca43/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/d638ca43/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/d638ca43/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/d638ca43/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/d638ca43/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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/FromString.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FromString.java 
b/src/main/groovy/groovy/transform/stc/FromString.java
new file mode 100644
index 0000000..12f9371
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FromString.java
@@ -0,0 +1,80 @@
+/*
+ *  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.tools.GenericsUtils;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * <p>A closure parameter hint class that is convenient if you want to use a 
String representation
+ * of the signature. It makes use of the {@link ClosureParams#options() option 
strings}, where
+ * each string corresponds to a single signature.</p>
+ *
+ * <p>The following example describes a closure as accepting a single 
signature (List&lt;T&gt; list -&gt;):</p>
+ *
+ * <code>public &lt;T&gt; T apply(T src, 
@ClosureParams(value=FromString.class, options="List&lt;T&gt;" Closure&lt;T&gt; 
cl)</code>
+ *
+ * <p>The next example describes a closure as accepting two signatures 
(List&lt;T&gt; list -&gt;) and (T t -&gt;):</p>
+ *
+ * <code>public &lt;T&gt; T apply(T src, 
@ClosureParams(value=FromString.class, options={"List&lt;T&gt;","T"} 
Closure&lt;T&gt; cl)</code>
+ *
+ * <p>It is advisable not to use this hint as a replacement for the various 
{@link FirstParam}, {@link SimpleType},
+ * ... hints because it is actually much slower. Using this hint should 
therefore be limited
+ * to cases where it's not possible to express the signature using the 
existing hints.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FromString extends ClosureSignatureHint {
+
+    @Override
+    public List<ClassNode[]> getClosureSignatures(final MethodNode node, final 
SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] 
options, final ASTNode usage) {
+        List<ClassNode[]> list = new ArrayList<ClassNode[]>(options.length);
+        for (String option : options) {
+            list.add(parseOption(option, sourceUnit, compilationUnit, node, 
usage));
+        }
+        return list;
+    }
+
+    /**
+     * Parses a string representing a type, that must be aligned with the 
current context.
+     * For example, <i>"List&lt;T&gt;"</i> must be converted into the 
appropriate ClassNode
+     * for which <i>T</i> matches the appropriate placeholder.
+     *
+     *
+     * @param option a string representing a type
+     * @param sourceUnit the source unit (of the file being compiled)
+     * @param compilationUnit the compilation unit (of the file being compiled)
+     * @param mn the method node
+     * @param usage
+     * @return a class node if it could be parsed and resolved, null otherwise
+     */
+    private static ClassNode[] parseOption(final String option, final 
SourceUnit sourceUnit, final CompilationUnit compilationUnit, final MethodNode 
mn, final ASTNode usage) {
+        return GenericsUtils.parseClassNodesFromString(option, sourceUnit, 
compilationUnit, mn, usage);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
----------------------------------------------------------------------
diff --git 
a/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java 
b/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
new file mode 100644
index 0000000..aed167a
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
@@ -0,0 +1,32 @@
+/*
+ *  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.MethodNode;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+public class IncorrectTypeHintException extends SyntaxException {
+    public IncorrectTypeHintException(final MethodNode mn, final Throwable e, 
int line, int column) {
+        super("Incorrect type hint in @ClosureParams in class 
"+mn.getDeclaringClass().getName()+" method "+mn.getTypeDescriptor()+" : 
"+e.getMessage(), e, line, column);
+    }
+
+    public IncorrectTypeHintException(final MethodNode mn, final String msg, 
final int line, final int column) {
+        super("Incorrect type hint in @ClosureParams in class 
"+mn.getDeclaringClass().getName()+" method "+mn.getTypeDescriptor()+" : "+msg, 
line, column);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java 
b/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
new file mode 100644
index 0000000..9316ef8
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
@@ -0,0 +1,119 @@
+/*
+ *  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.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>A special hint which handles a common use case in the Groovy methods 
that work on maps. In case of an
+ * iteration on a list of map entries, you often want the user to be able to 
work either on a {@link java.util.Map.Entry} map entry
+ * or on a key,value pair.</p>
+ * <p>The result is a closure which can have the following forms:</p>
+ * <ul>
+ *     <li><code>{ key, value -> ...}</code> where key is the key of a map 
entry, and value the corresponding value</li>
+ *     <li><code>{ entry -> ... }</code> where entry is a {@link 
java.util.Map.Entry} map entry</li>
+ *     <li><code>{ ...}</code> where <i>it</i> is an implicit {@link 
java.util.Map.Entry} map entry</li>
+ * </ul>
+ * <p>This hint handles all those cases by picking the generics from the first 
argument of the method (by default).</p>
+ * <p>The options array is used to modify the behavior of this hint. Each 
string in the option array consists of
+ * a key=value pair.</p>
+ * <ul>
+ *     <li><i>argNum=index</i> of the parameter representing the map (by 
default, 0)</li>
+ *     <li><i>index=true or false</i>, by default false. If true, then an 
additional "int" parameter is added,
+ *     for "withIndex" variants</li>
+ * </ul>
+ * <code>void doSomething(String str, Map&lt;K,&gt;V map, 
@ClosureParams(value=MapEntryOrKeyValue.class,options="argNum=1") Closure c) { 
... }</code>
+ */
+public class MapEntryOrKeyValue extends ClosureSignatureHint {
+    private static final ClassNode MAPENTRY_TYPE = 
ClassHelper.make(Map.Entry.class);
+
+    public List<ClassNode[]> getClosureSignatures(final MethodNode node, final 
SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] 
options, final ASTNode usage) {
+        Options opt;
+        try {
+            opt = Options.parse(node, usage, options);
+        } catch (IncorrectTypeHintException e) {
+            sourceUnit.addError(e);
+            return Collections.emptyList();
+        }
+        GenericsType[] genericsTypes = 
node.getParameters()[opt.parameterIndex].getOriginType().getGenericsTypes();
+        if (genericsTypes==null) {
+            // would happen if you have a raw Map type for example
+            genericsTypes = new GenericsType[] {
+                new GenericsType(ClassHelper.OBJECT_TYPE),
+                new GenericsType(ClassHelper.OBJECT_TYPE)
+            };
+        }
+        ClassNode[] firstSig;
+        ClassNode[] secondSig;
+        ClassNode mapEntry = MAPENTRY_TYPE.getPlainNodeReference();
+        mapEntry.setGenericsTypes(genericsTypes);
+        if (opt.generateIndex) {
+            firstSig = new ClassNode[] {genericsTypes[0].getType(), 
genericsTypes[1].getType(), ClassHelper.int_TYPE};
+            secondSig = new ClassNode[] {mapEntry, ClassHelper.int_TYPE};
+
+        } else {
+            firstSig = new ClassNode[] {genericsTypes[0].getType(), 
genericsTypes[1].getType()};
+            secondSig = new ClassNode[] {mapEntry};
+        }
+        return Arrays.asList(firstSig, secondSig);
+    }
+    
+    private static final class Options {
+        final int parameterIndex;
+        final boolean generateIndex;
+
+        private Options(final int parameterIndex, final boolean generateIndex) 
{
+            this.parameterIndex = parameterIndex;
+            this.generateIndex = generateIndex;
+        }
+        
+        static Options parse(MethodNode mn, ASTNode source, String[] options) 
throws IncorrectTypeHintException {
+            int pIndex = 0;
+            boolean generateIndex = false;
+            for (String option : options) {
+                String[] keyValue = option.split("=");
+                if (keyValue.length==2) {
+                    String key = keyValue[0];
+                    String value = keyValue[1];
+                    if ("argNum".equals(key)) {
+                        pIndex = Integer.parseInt(value);
+                    } else if ("index".equals(key)) {
+                        generateIndex = Boolean.valueOf(value);
+                    } else {
+                        throw new IncorrectTypeHintException(mn, "Unrecognized 
option: "+key, source.getLineNumber(), source.getColumnNumber());
+                    }
+                } else {
+                    throw new IncorrectTypeHintException(mn, "Incorrect option 
format. Should be argNum=<num> or index=<boolean> ", source.getLineNumber(), 
source.getColumnNumber());
+                }
+            }
+            return new Options(pIndex, generateIndex);
+        }
+    } 
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java 
b/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
new file mode 100644
index 0000000..6a5463f
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
@@ -0,0 +1,75 @@
+/*
+ *  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>Base class for hints which use the type of a parameter of the annotated 
method as the signature.
+ * This can optionally use a generic type of the selected parameter as the 
hint. For example, imagine the following
+ * method:</p>
+ * <code>void foo(A firstArg, B secondArg, Closure c) {...}</code>
+ * <p>If the <i>c</i> closure should be <code>{ B it -> ...}</code>, then we 
can see that the parameter type
+ * should be picked from the second parameter of the foo method, which is what 
{@link groovy.transform.stc.PickAnyArgumentHint}
+ * lets you do.</p>
+ * <p>Alternatively, the method may look like this:</p>
+ * <code>void &lt;T&gt; foo(A&lt;T&gt; firstArg, B secondArg, Closure c) 
{...}</code>
+ * <p>in which case if you want to express the fact that <i>c</i> should 
accept a &lt;T&gt; then you can use the
+ * {@link #genericTypeIndex} value.</p>
+ * <p></p>
+ * <p>This class is extended by several hint providers that make it easier to 
use as annotation values.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class PickAnyArgumentHint extends SingleSignatureClosureHint {
+    private final int parameterIndex;
+    private final int genericTypeIndex;
+
+    /**
+     * Creates the an argument picker which extracts the type of the first 
parameter.
+     */
+    public PickAnyArgumentHint() {
+        this(0,-1);
+    }
+
+    /**
+     * Creates a picker which will extract the parameterIndex-th parameter 
type, or its
+     * genericTypeIndex-th generic type genericTypeIndex is &gt;=0.
+     * @param parameterIndex the index of the parameter from which to extract 
the type
+     * @param genericTypeIndex if &gt;=0, then returns the corresponding 
generic type instead of the parameter type.
+     */
+    public PickAnyArgumentHint(final int parameterIndex, final int 
genericTypeIndex) {
+        this.parameterIndex = parameterIndex;
+        this.genericTypeIndex = genericTypeIndex;
+    }
+
+    @Override
+    public ClassNode[] getParameterTypes(final MethodNode node, final String[] 
options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode 
usage) {
+        ClassNode type = node.getParameters()[parameterIndex].getOriginType();
+        if (genericTypeIndex>=0) {
+            type = pickGenericType(type, genericTypeIndex);
+        }
+        return new ClassNode[]{type};
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/PickFirstResolver.java 
b/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
new file mode 100644
index 0000000..0bebb3e
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
@@ -0,0 +1,45 @@
+/*
+ *  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.Collections;
+import java.util.List;
+
+/**
+ * Returns the first of several candidates found.
+ * This is useful if several types should be supported but only the first
+ * should be the default/inferred type. Other options in the list are
+ * obtained through explicitly typing the parameter(s).
+ *
+ * @since 2.5.0
+ */
+public class PickFirstResolver extends ClosureSignatureConflictResolver {
+    @Override
+    public List<ClassNode[]> resolve(List<ClassNode[]> candidates, ClassNode 
receiver, Expression arguments, ClosureExpression closure,
+                                     MethodNode methodNode, SourceUnit 
sourceUnit, CompilationUnit compilationUnit, String[] options) {
+        return Collections.singletonList(candidates.get(0));
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/SecondParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/SecondParam.java 
b/src/main/groovy/groovy/transform/stc/SecondParam.java
new file mode 100644
index 0000000..c077750
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SecondParam.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 second parameter 
type. For example:</p>
+ * <code>public &lt;T,U&gt; def doWith(T first, U second, 
@ClosureParams(SecondParam.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 SecondParam extends PickAnyArgumentHint {
+    public SecondParam() {
+        super(1,-1);
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the first generic 
type of the second parameter type. For example:</p>
+     * <code>void &lt;T&gt; doWithElements(String base, List&lt;T&gt; src, 
@ClosureParams(SecondParam.FirstGenericType.class) Closure c) { ... } }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class FirstGenericType extends PickAnyArgumentHint {
+        public FirstGenericType() {
+            super(1,0);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the second generic 
type of the second parameter type. For example:</p>
+     * <code>void &lt;T,U&gt; doWithElements(String base, Tuple&lt;T,U&gt; 
src, @ClosureParams(SecondParam.SecondGenericType.class) Closure c) { ... 
}</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class SecondGenericType extends PickAnyArgumentHint {
+        public SecondGenericType() {
+            super(1,1);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the second generic 
type of the second parameter type. For example:</p>
+     * <code>void &lt;T,U,V&gt; doWithElements(String base, 
Triple&lt;T,U,V&gt; src, @ClosureParams(SecondParam.ThirdGenericType.class) 
Closure c) { ... }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class ThirdGenericType extends PickAnyArgumentHint {
+        public ThirdGenericType() {
+            super(1,2);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the type of the 
component of the second parameter type, which is therefore
+     * expected to be an array, like in this example:</p>
+     * <code>void &lt;T&gt; doWithArray(String first, T[] array, 
@ClosureParams(FirstParam.Component.class) Closure c) { ... }</code>
+     */
+    public static class Component extends SecondParam {
+        @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/d638ca43/src/main/groovy/groovy/transform/stc/SimpleType.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/SimpleType.java 
b/src/main/groovy/groovy/transform/stc/SimpleType.java
new file mode 100644
index 0000000..8866e3b
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SimpleType.java
@@ -0,0 +1,36 @@
+/*
+ *  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;
+
+public class SimpleType extends SingleSignatureClosureHint {
+    @Override
+    public ClassNode[] getParameterTypes(final MethodNode node, final String[] 
options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode 
usage) {
+        ClassNode[] result = new ClassNode[options.length];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = findClassNode(sourceUnit, unit, options[i]);
+        }
+        return result;
+    }
+ }

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java
----------------------------------------------------------------------
diff --git 
a/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java 
b/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java
new file mode 100644
index 0000000..3db66ff
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java
@@ -0,0 +1,44 @@
+/*
+ *  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;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A simplified version of a {@link groovy.transform.stc.ClosureSignatureHint} 
which is suitable
+ * for monomorphic closures, that is to say closures which only respond to a 
single signature.
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public abstract class SingleSignatureClosureHint extends ClosureSignatureHint {
+
+    public abstract ClassNode[] getParameterTypes(final MethodNode node, final 
String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, 
final ASTNode usage);
+
+    public List<ClassNode[]> getClosureSignatures(final MethodNode node, final 
SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] 
options, final ASTNode usage) {
+        return Collections.singletonList(getParameterTypes(node, options, 
sourceUnit, compilationUnit, usage));
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/d638ca43/src/main/groovy/groovy/transform/stc/ThirdParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ThirdParam.java 
b/src/main/groovy/groovy/transform/stc/ThirdParam.java
new file mode 100644
index 0000000..c493e36
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ThirdParam.java
@@ -0,0 +1,94 @@
+/*
+ *  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 third parameter 
type. For example:</p>
+ * <code>public &lt;T,U,V&gt; def doWith(T first, U second, V third, 
@ClosureParams(ThirdParam.class) Closure c) { ... }</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 ThirdParam extends PickAnyArgumentHint {
+    public ThirdParam() {
+        super(2,-1);
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the first generic 
type of the third parameter type. For example:</p>
+     * <code>void &lt;T&gt; doWithElements(String first, Integer second, 
List&lt;T&gt; third, @ClosureParams(SecondParam.FirstGenericType.class) Closure 
c) { ... } }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class FirstGenericType extends PickAnyArgumentHint {
+        public FirstGenericType() {
+            super(2,0);
+        }
+    }
+
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the second generic 
type of the third parameter type. For example:</p>
+     * <code>void &lt;T,U&gt; doWithElements(String first, Integer second, 
Tuple&lt;T,U&gt; third, @ClosureParams(SecondParam.SecondGenericType.class) 
Closure c) { ... }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class SecondGenericType extends PickAnyArgumentHint {
+        public SecondGenericType() {
+            super(2,1);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the second generic 
type of the third parameter type. For example:</p>
+     * <code>void &lt;T,U,V&gt; doWithElements(String first, Integer second, 
Triple&lt;T,U,V&gt; src, @ClosureParams(SecondParam.ThirdGenericType.class) 
Closure c) { ... }</code>
+     *
+     * @author Cédric Champeau
+     * @since 2.3.0
+     */
+    public static class ThirdGenericType extends PickAnyArgumentHint {
+        public ThirdGenericType() {
+            super(2,2);
+        }
+    }
+
+    /**
+     * <p>A hint used to instruct the type checker to pick the type of the 
component of the third parameter type, which is therefore
+     * expected to be an array, like in this example:</p>
+     * <code>void &lt;T&gt; doWithArray(String first, int second, T[] third, 
@ClosureParams(FirstParam.Component.class) Closure c) { ... }</code>
+     */
+    public static class Component extends ThirdParam {
+        @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;
+        }
+    }
+}

Reply via email to