You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:42 UTC

[31/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
new file mode 100644
index 0000000..b2beb96
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -0,0 +1,805 @@
+/*
+ *  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 org.codehaus.groovy.ast.tools;
+
+import org.codehaus.groovy.ast.ASTNode;
+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.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.PackageNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.runtime.GeneratedClosure;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_IDENTICAL;
+
+/**
+ * Handy methods when working with the Groovy AST
+ */
+public class GeneralUtils {
+    public static final Token ASSIGN = Token.newSymbol(Types.ASSIGN, -1, -1);
+    public static final Token EQ = Token.newSymbol(Types.COMPARE_EQUAL, -1, -1);
+    public static final Token NE = Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1);
+    public static final Token NOT_IDENTICAL = Token.newSymbol(COMPARE_NOT_IDENTICAL, -1, -1);
+    public static final Token LT = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1);
+    public static final Token AND = Token.newSymbol(Types.LOGICAL_AND, -1, -1);
+    public static final Token OR = Token.newSymbol(Types.LOGICAL_OR, -1, -1);
+    public static final Token CMP = Token.newSymbol(Types.COMPARE_TO, -1, -1);
+    private static final Token INSTANCEOF = Token.newSymbol(Types.KEYWORD_INSTANCEOF, -1, -1);
+    private static final Token PLUS = Token.newSymbol(Types.PLUS, -1, -1);
+    private static final Token INDEX = Token.newSymbol("[", -1, -1);
+
+    public static BinaryExpression andX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, AND, rhv);
+    }
+
+    public static ArgumentListExpression args(Expression... expressions) {
+        List<Expression> args = new ArrayList<Expression>();
+        Collections.addAll(args, expressions);
+        return new ArgumentListExpression(args);
+    }
+
+    public static ArgumentListExpression args(List<Expression> expressions) {
+        return new ArgumentListExpression(expressions);
+    }
+
+    public static ArgumentListExpression args(Parameter[] parameters) {
+        return new ArgumentListExpression(parameters);
+    }
+
+    public static ArgumentListExpression args(String... names) {
+        List<Expression> vars = new ArrayList<Expression>();
+        for (String name : names) {
+            vars.add(varX(name));
+        }
+        return new ArgumentListExpression(vars);
+    }
+
+    public static Statement assignS(Expression target, Expression value) {
+        return new ExpressionStatement(assignX(target, value));
+    }
+
+    public static Expression assignX(Expression target, Expression value) {
+        return new BinaryExpression(target, ASSIGN, value);
+    }
+
+    public static Expression attrX(Expression oe, Expression prop) {
+        return new AttributeExpression(oe, prop);
+    }
+
+    public static BinaryExpression binX(Expression left, Token token, Expression right) {
+        return new BinaryExpression(left, token, right);
+    }
+
+    public static BlockStatement block(VariableScope varScope, Statement... stmts) {
+        BlockStatement block = new BlockStatement();
+        block.setVariableScope(varScope);
+        for (Statement stmt : stmts) block.addStatement(stmt);
+        return block;
+    }
+
+    public static BlockStatement block(VariableScope varScope, List<Statement> stmts) {
+        BlockStatement block = new BlockStatement();
+        block.setVariableScope(varScope);
+        for (Statement stmt : stmts) block.addStatement(stmt);
+        return block;
+    }
+
+    public static BlockStatement block(Statement... stmts) {
+        BlockStatement block = new BlockStatement();
+        for (Statement stmt : stmts) block.addStatement(stmt);
+        return block;
+    }
+
+    public static MethodCallExpression callSuperX(String methodName, Expression args) {
+        return callX(varX("super"), methodName, args);
+    }
+
+    public static MethodCallExpression callSuperX(String methodName) {
+        return callSuperX(methodName, MethodCallExpression.NO_ARGUMENTS);
+    }
+
+    public static MethodCallExpression callThisX(String methodName, Expression args) {
+        return callX(varX("this"), methodName, args);
+    }
+
+    public static MethodCallExpression callThisX(String methodName) {
+        return callThisX(methodName, MethodCallExpression.NO_ARGUMENTS);
+    }
+
+    public static MethodCallExpression callX(Expression receiver, String methodName, Expression args) {
+        return new MethodCallExpression(receiver, methodName, args);
+    }
+
+    public static MethodCallExpression callX(Expression receiver, Expression method, Expression args) {
+        return new MethodCallExpression(receiver, method, args);
+    }
+
+    public static MethodCallExpression callX(Expression receiver, String methodName) {
+        return callX(receiver, methodName, MethodCallExpression.NO_ARGUMENTS);
+    }
+
+    public static StaticMethodCallExpression callX(ClassNode receiver, String methodName, Expression args) {
+        return new StaticMethodCallExpression(receiver, methodName, args);
+    }
+
+    public static StaticMethodCallExpression callX(ClassNode receiver, String methodName) {
+        return callX(receiver, methodName, MethodCallExpression.NO_ARGUMENTS);
+    }
+
+    public static CastExpression castX(ClassNode type, Expression expression) {
+        return new CastExpression(type, expression);
+    }
+
+    public static CastExpression castX(ClassNode type, Expression expression, boolean ignoreAutoboxing) {
+        return new CastExpression(type, expression, ignoreAutoboxing);
+    }
+
+    public static ClassExpression classX(ClassNode clazz) {
+        return new ClassExpression(clazz);
+    }
+
+    public static ClassExpression classX(Class clazz) {
+        return classX(ClassHelper.make(clazz).getPlainNodeReference());
+    }
+
+    public static ClosureExpression closureX(Parameter[] params, Statement code) {
+        return new ClosureExpression(params, code);
+    }
+
+    public static ClosureExpression closureX(Statement code) {
+        return closureX(Parameter.EMPTY_ARRAY, code);
+    }
+
+    public static Parameter[] cloneParams(Parameter[] source) {
+        Parameter[] result = new Parameter[source.length];
+        for (int i = 0; i < source.length; i++) {
+            Parameter srcParam = source[i];
+            Parameter dstParam = new Parameter(srcParam.getOriginType(), srcParam.getName());
+            result[i] = dstParam;
+        }
+        return result;
+    }
+
+    /**
+     * Build a binary expression that compares two values
+     * @param lhv expression for the value to compare from
+     * @param rhv expression for the value value to compare to
+     * @return the expression comparing two values
+     */
+    public static BinaryExpression cmpX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, CMP, rhv);
+    }
+
+    public static ConstantExpression constX(Object val) {
+        return new ConstantExpression(val);
+    }
+
+    public static ConstantExpression constX(Object val, boolean keepPrimitive) {
+        return new ConstantExpression(val, keepPrimitive);
+    }
+
+    /**
+     * Copies all <tt>candidateAnnotations</tt> with retention policy {@link java.lang.annotation.RetentionPolicy#RUNTIME}
+     * and {@link java.lang.annotation.RetentionPolicy#CLASS}.
+     * <p>
+     * Annotations with {@link org.codehaus.groovy.runtime.GeneratedClosure} members are not supported at present.
+     */
+    public static void copyAnnotatedNodeAnnotations(final AnnotatedNode annotatedNode, final List<AnnotationNode> copied, List<AnnotationNode> notCopied) {
+        List<AnnotationNode> annotationList = annotatedNode.getAnnotations();
+        for (AnnotationNode annotation : annotationList)  {
+
+            List<AnnotationNode> annotations = annotation.getClassNode().getAnnotations(AbstractASTTransformation.RETENTION_CLASSNODE);
+            if (annotations.isEmpty()) continue;
+
+            if (hasClosureMember(annotation)) {
+                notCopied.add(annotation);
+                continue;
+            }
+
+            AnnotationNode retentionPolicyAnnotation = annotations.get(0);
+            Expression valueExpression = retentionPolicyAnnotation.getMember("value");
+            if (!(valueExpression instanceof PropertyExpression)) continue;
+
+            PropertyExpression propertyExpression = (PropertyExpression) valueExpression;
+            boolean processAnnotation =
+                    propertyExpression.getProperty() instanceof ConstantExpression &&
+                            (
+                                    "RUNTIME".equals(((ConstantExpression) (propertyExpression.getProperty())).getValue()) ||
+                                            "CLASS".equals(((ConstantExpression) (propertyExpression.getProperty())).getValue())
+                            );
+
+            if (processAnnotation)  {
+                AnnotationNode newAnnotation = new AnnotationNode(annotation.getClassNode());
+                for (Map.Entry<String, Expression> member : annotation.getMembers().entrySet())  {
+                    newAnnotation.addMember(member.getKey(), member.getValue());
+                }
+                newAnnotation.setSourcePosition(annotatedNode);
+
+                copied.add(newAnnotation);
+            }
+        }
+    }
+
+    public static Statement createConstructorStatementDefault(FieldNode fNode) {
+        final String name = fNode.getName();
+        final ClassNode fType = fNode.getType();
+        final Expression fieldExpr = propX(varX("this"), name);
+        Expression initExpr = fNode.getInitialValueExpression();
+        Statement assignInit;
+        if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression)initExpr).isNullExpression())) {
+            if (ClassHelper.isPrimitiveType(fType)) {
+                assignInit = EmptyStatement.INSTANCE;
+            } else {
+                assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION);
+            }
+        } else {
+            assignInit = assignS(fieldExpr, initExpr);
+        }
+        fNode.setInitialValueExpression(null);
+        Expression value = findArg(name);
+        return ifElseS(equalsNullX(value), assignInit, assignS(fieldExpr, castX(fType, value)));
+    }
+
+    public static ConstructorCallExpression ctorX(ClassNode type, Expression args) {
+        return new ConstructorCallExpression(type, args);
+    }
+
+    public static ConstructorCallExpression ctorX(ClassNode type) {
+        return new ConstructorCallExpression(type, ArgumentListExpression.EMPTY_ARGUMENTS);
+    }
+
+    public static Statement ctorSuperS(Expression args) {
+        return stmt(ctorX(ClassNode.SUPER, args));
+    }
+
+    public static Statement ctorThisS(Expression args) {
+        return stmt(ctorX(ClassNode.THIS, args));
+    }
+
+    public static Statement ctorSuperS() {
+        return stmt(ctorX(ClassNode.SUPER));
+    }
+
+    public static Statement ctorThisS() {
+        return stmt(ctorX(ClassNode.THIS));
+    }
+
+    public static Statement declS(Expression target, Expression init) {
+        return new ExpressionStatement(new DeclarationExpression(target, ASSIGN, init));
+    }
+
+    public static BinaryExpression eqX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, EQ, rhv);
+    }
+
+    public static BooleanExpression equalsNullX(Expression argExpr) {
+        return new BooleanExpression(eqX(argExpr, new ConstantExpression(null)));
+    }
+
+    public static FieldExpression fieldX(FieldNode fieldNode) {
+        return new FieldExpression(fieldNode);
+    }
+
+    public static FieldExpression fieldX(ClassNode owner, String fieldName) {
+        return new FieldExpression(owner.getField(fieldName));
+    }
+
+    public static Expression findArg(String argName) {
+        return new PropertyExpression(new VariableExpression("args"), argName);
+    }
+
+    public static List<MethodNode> getAllMethods(ClassNode type) {
+        ClassNode node = type;
+        List<MethodNode> result = new ArrayList<MethodNode>();
+        while (node != null) {
+            result.addAll(node.getMethods());
+            node = node.getSuperClass();
+        }
+        return result;
+    }
+
+    public static List<PropertyNode> getAllProperties(ClassNode type) {
+        ClassNode node = type;
+        List<PropertyNode> result = new ArrayList<PropertyNode>();
+        while (node != null) {
+            result.addAll(node.getProperties());
+            node = node.getSuperClass();
+        }
+        return result;
+    }
+
+    public static String getGetterName(PropertyNode pNode) {
+        return "get" + Verifier.capitalize(pNode.getName());
+    }
+
+    public static List<FieldNode> getInstanceNonPropertyFields(ClassNode cNode) {
+        final List<FieldNode> result = new ArrayList<FieldNode>();
+        for (FieldNode fNode : cNode.getFields()) {
+            if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) {
+                result.add(fNode);
+            }
+        }
+        return result;
+    }
+
+    public static List<String> getInstanceNonPropertyFieldNames(ClassNode cNode) {
+        List<FieldNode> fList = getInstanceNonPropertyFields(cNode);
+        List<String> result = new ArrayList<String>(fList.size());
+        for (FieldNode fNode : fList) {
+            result.add(fNode.getName());
+        }
+        return result;
+    }
+
+    public static List<PropertyNode> getInstanceProperties(ClassNode cNode) {
+        final List<PropertyNode> result = new ArrayList<PropertyNode>();
+        for (PropertyNode pNode : cNode.getProperties()) {
+            if (!pNode.isStatic()) {
+                result.add(pNode);
+            }
+        }
+        return result;
+    }
+
+    public static List<String> getInstancePropertyNames(ClassNode cNode) {
+        List<PropertyNode> pList = BeanUtils.getAllProperties(cNode, false, false, true);
+        List<String> result = new ArrayList<String>(pList.size());
+        for (PropertyNode pNode : pList) {
+            result.add(pNode.getName());
+        }
+        return result;
+    }
+
+    public static List<FieldNode> getInstancePropertyFields(ClassNode cNode) {
+        final List<FieldNode> result = new ArrayList<FieldNode>();
+        for (PropertyNode pNode : cNode.getProperties()) {
+            if (!pNode.isStatic()) {
+                result.add(pNode.getField());
+            }
+        }
+        return result;
+    }
+
+    public static Set<ClassNode> getInterfacesAndSuperInterfaces(ClassNode type) {
+        Set<ClassNode> res = new LinkedHashSet<ClassNode>();
+        if (type.isInterface()) {
+            res.add(type);
+            return res;
+        }
+        ClassNode next = type;
+        while (next != null) {
+            res.addAll(next.getAllInterfaces());
+            next = next.getSuperClass();
+        }
+        return res;
+    }
+
+    public static List<FieldNode> getSuperNonPropertyFields(ClassNode cNode) {
+        final List<FieldNode> result;
+        if (cNode == ClassHelper.OBJECT_TYPE) {
+            result = new ArrayList<FieldNode>();
+        } else {
+            result = getSuperNonPropertyFields(cNode.getSuperClass());
+        }
+        for (FieldNode fNode : cNode.getFields()) {
+            if (!fNode.isStatic() && cNode.getProperty(fNode.getName()) == null) {
+                result.add(fNode);
+            }
+        }
+        return result;
+    }
+
+    public static List<FieldNode> getSuperPropertyFields(ClassNode cNode) {
+        final List<FieldNode> result;
+        if (cNode == ClassHelper.OBJECT_TYPE) {
+            result = new ArrayList<FieldNode>();
+        } else {
+            result = getSuperPropertyFields(cNode.getSuperClass());
+        }
+        for (PropertyNode pNode : cNode.getProperties()) {
+            if (!pNode.isStatic()) {
+                result.add(pNode.getField());
+            }
+        }
+        return result;
+    }
+
+    public static BinaryExpression hasClassX(Expression instance, ClassNode cNode) {
+        return eqX(classX(cNode), callX(instance, "getClass"));
+    }
+
+    private static boolean hasClosureMember(AnnotationNode annotation) {
+
+        Map<String, Expression> members = annotation.getMembers();
+        for (Map.Entry<String, Expression> member : members.entrySet())  {
+            if (member.getValue() instanceof ClosureExpression) return true;
+
+            if (member.getValue() instanceof ClassExpression)  {
+                ClassExpression classExpression = (ClassExpression) member.getValue();
+                Class<?> typeClass = classExpression.getType().isResolved() ? classExpression.getType().redirect().getTypeClass() : null;
+                if (typeClass != null && GeneratedClosure.class.isAssignableFrom(typeClass)) return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static boolean hasDeclaredMethod(ClassNode cNode, String name, int argsCount) {
+        List<MethodNode> ms = cNode.getDeclaredMethods(name);
+        for (MethodNode m : ms) {
+            Parameter[] paras = m.getParameters();
+            if (paras != null && paras.length == argsCount) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static BinaryExpression hasEqualFieldX(FieldNode fNode, Expression other) {
+        return eqX(varX(fNode), propX(other, fNode.getName()));
+    }
+
+    public static BinaryExpression hasEqualPropertyX(ClassNode annotatedNode, PropertyNode pNode, VariableExpression other) {
+        return eqX(getterThisX(annotatedNode, pNode), getterX(other.getOriginType(), other, pNode));
+    }
+
+    @Deprecated
+    public static BinaryExpression hasEqualPropertyX(PropertyNode pNode, Expression other) {
+        String getterName = getGetterName(pNode);
+        return eqX(callThisX(getterName), callX(other, getterName));
+    }
+
+    public static BooleanExpression hasSameFieldX(FieldNode fNode, Expression other) {
+        return sameX(varX(fNode), propX(other, fNode.getName()));
+    }
+
+    public static BooleanExpression hasSamePropertyX(PropertyNode pNode, Expression other) {
+        ClassNode cNode = pNode.getDeclaringClass();
+        return sameX(getterThisX(cNode, pNode), getterX(cNode, other, pNode));
+    }
+
+    public static Statement ifElseS(Expression cond, Statement thenStmt, Statement elseStmt) {
+        return new IfStatement(
+                cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
+                thenStmt,
+                elseStmt
+        );
+    }
+
+    public static Statement ifS(Expression cond, Expression trueExpr) {
+        return ifS(cond, new ExpressionStatement(trueExpr));
+    }
+
+    public static Statement ifS(Expression cond, Statement trueStmt) {
+        return new IfStatement(
+                cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
+                trueStmt,
+                EmptyStatement.INSTANCE
+        );
+    }
+
+    public static Expression indexX(Expression target, Expression value) {
+        return new BinaryExpression(target, INDEX, value);
+    }
+
+    public static BooleanExpression isInstanceOfX(Expression objectExpression, ClassNode cNode) {
+        return new BooleanExpression(new BinaryExpression(objectExpression, INSTANCEOF, classX(cNode)));
+    }
+
+    public static BooleanExpression isOneX(Expression expr) {
+        return new BooleanExpression(new BinaryExpression(expr, EQ, new ConstantExpression(1)));
+    }
+
+    public static boolean isOrImplements(ClassNode type, ClassNode interfaceType) {
+        return type.equals(interfaceType) || type.implementsInterface(interfaceType);
+    }
+
+    public static BooleanExpression isTrueX(Expression argExpr) {
+        return new BooleanExpression(new BinaryExpression(argExpr, EQ, new ConstantExpression(Boolean.TRUE)));
+    }
+
+    public static BooleanExpression isZeroX(Expression expr) {
+        return new BooleanExpression(new BinaryExpression(expr, EQ, new ConstantExpression(0)));
+    }
+
+    public static BinaryExpression ltX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, LT, rhv);
+    }
+
+    public static BinaryExpression notIdenticalX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, NOT_IDENTICAL, rhv);
+    }
+
+    /**
+     * @deprecated use MethodNodeUtils#methodDescriptorWithoutReturnType(MethodNode) instead
+     */
+    @Deprecated
+    public static String makeDescriptorWithoutReturnType(MethodNode mn) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(mn.getName()).append(':');
+        for (Parameter p : mn.getParameters()) {
+            sb.append(p.getType()).append(',');
+        }
+        return sb.toString();
+    }
+
+    public static BinaryExpression neX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, NE, rhv);
+    }
+
+    public static BooleanExpression notNullX(Expression argExpr) {
+        return new BooleanExpression(new BinaryExpression(argExpr, NE, new ConstantExpression(null)));
+    }
+
+    public static NotExpression notX(Expression expr) {
+        return new NotExpression(expr instanceof BooleanExpression ? expr : new BooleanExpression(expr));
+    }
+
+    public static BinaryExpression orX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, OR, rhv);
+    }
+
+    public static Parameter param(ClassNode type, String name) {
+        return param(type, name, null);
+    }
+
+    public static Parameter param(ClassNode type, String name, Expression initialExpression) {
+        Parameter param = new Parameter(type, name);
+        if (initialExpression != null) {
+            param.setInitialExpression(initialExpression);
+        }
+        return param;
+    }
+
+    public static Parameter[] params(Parameter... params) {
+        return params != null ? params : Parameter.EMPTY_ARRAY;
+    }
+
+    public static BinaryExpression plusX(Expression lhv, Expression rhv) {
+        return new BinaryExpression(lhv, PLUS, rhv);
+    }
+
+    public static Expression propX(Expression owner, String property) {
+        return new PropertyExpression(owner, property);
+    }
+
+    public static Expression propX(Expression owner, Expression property) {
+        return new PropertyExpression(owner, property);
+    }
+
+    public static Statement returnS(Expression expr) {
+        return new ReturnStatement(new ExpressionStatement(expr));
+    }
+
+    public static Statement safeExpression(Expression fieldExpr, Expression expression) {
+        return new IfStatement(
+                equalsNullX(fieldExpr),
+                new ExpressionStatement(fieldExpr),
+                new ExpressionStatement(expression));
+    }
+
+    public static BooleanExpression sameX(Expression self, Expression other) {
+        return new BooleanExpression(callX(self, "is", args(other)));
+    }
+
+    public static Statement stmt(Expression expr) {
+        return new ExpressionStatement(expr);
+    }
+
+    public static TernaryExpression ternaryX(Expression cond, Expression trueExpr, Expression elseExpr) {
+        return new TernaryExpression(
+                cond instanceof BooleanExpression ? (BooleanExpression) cond : new BooleanExpression(cond),
+                trueExpr,
+                elseExpr);
+    }
+
+    public static VariableExpression varX(String name) {
+        return new VariableExpression(name);
+    }
+
+    public static VariableExpression varX(Variable variable) {
+        return new VariableExpression(variable);
+    }
+
+    public static VariableExpression varX(String name, ClassNode type) {
+        return new VariableExpression(name, type);
+    }
+
+    public static ThrowStatement throwS(Expression expr) {
+        return new ThrowStatement(expr);
+    }
+
+    public static CatchStatement catchS(Parameter variable, Statement code) {
+        return new CatchStatement(variable, code);
+    }
+
+    /**
+     * This method is similar to {@link #propX(Expression, Expression)} but will make sure that if the property
+     * being accessed is defined inside the classnode provided as a parameter, then a getter call is generated
+     * instead of a field access.
+     * @param annotatedNode the class node where the property node is accessed from
+     * @param pNode the property being accessed
+     * @return a method call expression or a property expression
+     */
+    public static Expression getterThisX(ClassNode annotatedNode, PropertyNode pNode) {
+        ClassNode owner = pNode.getDeclaringClass();
+        if (annotatedNode.equals(owner)) {
+            return callThisX(getterName(annotatedNode, pNode));
+        }
+        return propX(new VariableExpression("this"), pNode.getName());
+    }
+
+    private static String getterName(ClassNode annotatedNode, PropertyNode pNode) {
+        String getterName = "get" + MetaClassHelper.capitalize(pNode.getName());
+        boolean existingExplicitGetter = annotatedNode.getMethod(getterName, Parameter.EMPTY_ARRAY) != null;
+        if (ClassHelper.boolean_TYPE.equals(pNode.getOriginType()) && !existingExplicitGetter) {
+            getterName = "is" + MetaClassHelper.capitalize(pNode.getName());
+        }
+        return getterName;
+    }
+
+    /**
+     * This method is similar to {@link #propX(Expression, Expression)} but will make sure that if the property
+     * being accessed is defined inside the classnode provided as a parameter, then a getter call is generated
+     * instead of a field access.
+     * @param annotatedNode the class node where the property node is accessed from
+     * @param receiver the object having the property
+     * @param pNode the property being accessed
+     * @return a method call expression or a property expression
+     */
+    public static Expression getterX(ClassNode annotatedNode, Expression receiver, PropertyNode pNode) {
+        ClassNode owner = pNode.getDeclaringClass();
+        if (annotatedNode.equals(owner)) {
+            return callX(receiver, getterName(annotatedNode, pNode));
+        }
+        return propX(receiver, pNode.getName());
+    }
+
+    /**
+     * Converts an expression into the String source. Only some specific expressions like closure expression
+     * support this.
+     *
+     * @param readerSource a source
+     * @param expression an expression. Can't be null
+     * @return the source the closure was created from
+     * @throws java.lang.IllegalArgumentException when expression is null
+     * @throws java.lang.Exception when closure can't be read from source
+     */
+    public static String convertASTToSource(ReaderSource readerSource, ASTNode expression) throws Exception {
+        if (expression == null) throw new IllegalArgumentException("Null: expression");
+
+        StringBuilder result = new StringBuilder();
+        for (int x = expression.getLineNumber(); x <= expression.getLastLineNumber(); x++) {
+            String line = readerSource.getLine(x, null);
+            if (line == null) {
+                throw new Exception(
+                        "Error calculating source code for expression. Trying to read line " + x + " from " + readerSource.getClass()
+                );
+            }
+            if (x == expression.getLastLineNumber()) {
+                line = line.substring(0, expression.getLastColumnNumber() - 1);
+            }
+            if (x == expression.getLineNumber()) {
+                line = line.substring(expression.getColumnNumber() - 1);
+            }
+            //restoring line breaks is important b/c of lack of semicolons
+            result.append(line).append('\n');
+        }
+
+
+        String source = result.toString().trim();
+
+        return source;
+    }
+
+    public static boolean copyStatementsWithSuperAdjustment(ClosureExpression pre, BlockStatement body) {
+        Statement preCode = pre.getCode();
+        boolean changed = false;
+        if (preCode instanceof BlockStatement) {
+            BlockStatement block = (BlockStatement) preCode;
+            List<Statement> statements = block.getStatements();
+            for (int i = 0; i < statements.size(); i++) {
+                Statement statement = statements.get(i);
+                // adjust the first statement if it's a super call
+                if (i == 0 && statement instanceof ExpressionStatement) {
+                    ExpressionStatement es = (ExpressionStatement) statement;
+                    Expression preExp = es.getExpression();
+                    if (preExp instanceof MethodCallExpression) {
+                        MethodCallExpression mce = (MethodCallExpression) preExp;
+                        String name = mce.getMethodAsString();
+                        if ("super".equals(name)) {
+                            es.setExpression(new ConstructorCallExpression(ClassNode.SUPER, mce.getArguments()));
+                            changed = true;
+                        }
+                    }
+                }
+                body.addStatement(statement);
+            }
+        }
+        return changed;
+    }
+
+    public static String getSetterName(String name) {
+        return "set" + Verifier.capitalize(name);
+    }
+
+    public static boolean isDefaultVisibility(int modifiers) {
+        return (modifiers & (Modifier.PRIVATE | Modifier.PUBLIC | Modifier.PROTECTED)) == 0;
+    }
+
+    public static boolean inSamePackage(ClassNode first, ClassNode second) {
+        PackageNode firstPackage = first.getPackage();
+        PackageNode secondPackage = second.getPackage();
+        return ((firstPackage == null && secondPackage == null) ||
+                        firstPackage != null && secondPackage != null && firstPackage.getName().equals(secondPackage.getName()));
+    }
+
+    public static boolean inSamePackage(Class first, Class second) {
+        Package firstPackage = first.getPackage();
+        Package secondPackage = second.getPackage();
+        return ((firstPackage == null && secondPackage == null) ||
+                        firstPackage != null && secondPackage != null && firstPackage.getName().equals(secondPackage.getName()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
new file mode 100644
index 0000000..653f327
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -0,0 +1,614 @@
+/*
+ *  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 org.codehaus.groovy.ast.tools;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import groovy.transform.stc.IncorrectTypeHintException;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.antlr.AntlrParserPlugin;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+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.ModuleNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.ResolveVisitor;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.ParserException;
+import org.codehaus.groovy.syntax.Reduction;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Utility methods to deal with generic types.
+ *
+ * @author Cedric Champeau
+ * @author Paul King
+ */
+public class GenericsUtils {
+    public static final GenericsType[] EMPTY_GENERICS_ARRAY = new GenericsType[0];
+
+    /**
+     * Given a parameterized type and a generic type information, aligns actual type parameters. For example, if a
+     * class uses generic type <pre>&lt;T,U,V&gt;</pre> (redirectGenericTypes), is used with actual type parameters
+     * <pre>&lt;java.lang.String, U,V&gt;</pre>, then a class or interface using generic types <pre>&lt;T,V&gt;</pre>
+     * will be aligned to <pre>&lt;java.lang.String,V&gt;</pre>
+     * @param redirectGenericTypes the type arguments or the redirect class node
+     * @param parameterizedTypes the actual type arguments used on this class node
+     * @param alignmentTarget the generic type arguments to which we want to align to
+     * @return aligned type arguments
+     * @deprecated You shouldn't call this method because it is inherently unreliable
+     */
+    @Deprecated
+    public static GenericsType[] alignGenericTypes(final GenericsType[] redirectGenericTypes, final GenericsType[] parameterizedTypes, final GenericsType[] alignmentTarget) {
+        if (alignmentTarget==null) return EMPTY_GENERICS_ARRAY;
+        if (parameterizedTypes==null || parameterizedTypes.length==0) return alignmentTarget;
+        GenericsType[] generics = new GenericsType[alignmentTarget.length];
+        for (int i = 0, scgtLength = alignmentTarget.length; i < scgtLength; i++) {
+            final GenericsType currentTarget = alignmentTarget[i];
+            GenericsType match = null;
+            if (redirectGenericTypes!=null) {
+                for (int j = 0; j < redirectGenericTypes.length && match == null; j++) {
+                    GenericsType redirectGenericType = redirectGenericTypes[j];
+                    if (redirectGenericType.isCompatibleWith(currentTarget.getType())) {
+                        if (currentTarget.isPlaceholder() && redirectGenericType.isPlaceholder() && !currentTarget.getName().equals(redirectGenericType.getName())) {
+                            // check if there's a potential better match
+                            boolean skip = false;
+                            for (int k=j+1; k<redirectGenericTypes.length && !skip; k++) {
+                                GenericsType ogt = redirectGenericTypes[k];
+                                if (ogt.isPlaceholder() && ogt.isCompatibleWith(currentTarget.getType()) && ogt.getName().equals(currentTarget.getName())) {
+                                    skip = true;
+                                }
+                            }
+                            if (skip) continue;
+                        }
+                        match = parameterizedTypes[j];
+                        if (currentTarget.isWildcard()) {
+                            // if alignment target is a wildcard type
+                            // then we must make best effort to return a parameterized
+                            // wildcard
+                            ClassNode lower = currentTarget.getLowerBound()!=null?match.getType():null;
+                            ClassNode[] currentUpper = currentTarget.getUpperBounds();
+                            ClassNode[] upper = currentUpper !=null?new ClassNode[currentUpper.length]:null;
+                            if (upper!=null) {
+                                for (int k = 0; k < upper.length; k++) {
+                                    upper[k] = currentUpper[k].isGenericsPlaceHolder()?match.getType():currentUpper[k];
+                                }
+                            }
+                            match = new GenericsType(ClassHelper.makeWithoutCaching("?"), upper, lower);
+                            match.setWildcard(true);
+                        }
+                    }
+                }
+            }
+            if (match == null) {
+                match = currentTarget;
+            }
+            generics[i]=match;
+        }
+        return generics;
+    }
+
+    /**
+     * Generates a wildcard generic type in order to be used for checks against class nodes.
+     * See {@link GenericsType#isCompatibleWith(org.codehaus.groovy.ast.ClassNode)}.
+     * @param types the type to be used as the wildcard upper bound
+     * @return a wildcard generics type
+     */
+    public static GenericsType buildWildcardType(final ClassNode... types) {
+        ClassNode base = ClassHelper.makeWithoutCaching("?");
+        GenericsType gt = new GenericsType(base, types, null);
+        gt.setWildcard(true);
+        return gt;
+    }
+
+    public static Map<String, GenericsType> extractPlaceholders(ClassNode cn) {
+        Map<String, GenericsType> ret = new HashMap<String, GenericsType>();
+        extractPlaceholders(cn, ret);
+        return ret;
+    }
+
+    /**
+     * For a given classnode, fills in the supplied map with the parameterized
+     * types it defines.
+     * @param node
+     * @param map
+     */
+    public static void extractPlaceholders(ClassNode node, Map<String, GenericsType> map) {
+        if (node == null) return;
+
+        if (node.isArray()) {
+            extractPlaceholders(node.getComponentType(), map);
+            return;
+        }
+
+        if (!node.isUsingGenerics() || !node.isRedirectNode()) return;
+        GenericsType[] parameterized = node.getGenericsTypes();
+        if (parameterized == null || parameterized.length == 0) return;
+        GenericsType[] redirectGenericsTypes = node.redirect().getGenericsTypes();
+        if (redirectGenericsTypes==null) redirectGenericsTypes = parameterized;
+        for (int i = 0; i < redirectGenericsTypes.length; i++) {
+            GenericsType redirectType = redirectGenericsTypes[i];
+            if (redirectType.isPlaceholder()) {
+                String name = redirectType.getName();
+                if (!map.containsKey(name)) {
+                    GenericsType value = parameterized[i];
+                    map.put(name, value);
+                    if (value.isWildcard()) {
+                        ClassNode lowerBound = value.getLowerBound();
+                        if (lowerBound!=null) {
+                            extractPlaceholders(lowerBound, map);
+                        }
+                        ClassNode[] upperBounds = value.getUpperBounds();
+                        if (upperBounds!=null) {
+                            for (ClassNode upperBound : upperBounds) {
+                                extractPlaceholders(upperBound, map);
+                            }
+                        }
+                    } else if (!value.isPlaceholder()) {
+                        extractPlaceholders(value.getType(), map);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Interface class nodes retrieved from {@link org.codehaus.groovy.ast.ClassNode#getInterfaces()}
+     * or {@link org.codehaus.groovy.ast.ClassNode#getAllInterfaces()} are returned with generic type
+     * arguments. This method allows returning a parameterized interface given the parameterized class
+     * node which implements this interface.
+     * @param hint the class node where generics types are parameterized
+     * @param target the interface we want to parameterize generics types
+     * @return a parameterized interface class node
+     * @deprecated Use #parameterizeType instead
+     */
+    @Deprecated
+    public static ClassNode parameterizeInterfaceGenerics(final ClassNode hint, final ClassNode target) {
+        return parameterizeType(hint, target);
+    }
+
+    /**
+     * Interface class nodes retrieved from {@link org.codehaus.groovy.ast.ClassNode#getInterfaces()}
+     * or {@link org.codehaus.groovy.ast.ClassNode#getAllInterfaces()} are returned with generic type
+     * arguments. This method allows returning a parameterized interface given the parameterized class
+     * node which implements this interface.
+     * @param hint the class node where generics types are parameterized
+     * @param target the interface we want to parameterize generics types
+     * @return a parameterized interface class node
+     */
+    public static ClassNode parameterizeType(final ClassNode hint, final ClassNode target) {
+        if (hint.isArray()) {
+            if (target.isArray()) {
+                return parameterizeType(hint.getComponentType(), target.getComponentType()).makeArray();
+            }
+            return target;
+        }
+        if (!target.equals(hint) && StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(target, hint)) {
+            ClassNode nextSuperClass = ClassHelper.getNextSuperClass(target, hint);
+            if (!hint.equals(nextSuperClass)) {
+                Map<String, ClassNode> genericsSpec = createGenericsSpec(hint);
+                extractSuperClassGenerics(hint, nextSuperClass, genericsSpec);
+                ClassNode result = correctToGenericsSpecRecurse(genericsSpec, nextSuperClass);
+                return parameterizeType(result, target);
+            }
+        }
+        Map<String, ClassNode> genericsSpec = createGenericsSpec(hint);
+        ClassNode targetRedirect = target.redirect();
+        genericsSpec = createGenericsSpec(targetRedirect, genericsSpec);
+        extractSuperClassGenerics(hint, targetRedirect, genericsSpec);
+        return correctToGenericsSpecRecurse(genericsSpec, targetRedirect);
+
+    }
+
+    public static ClassNode nonGeneric(ClassNode type) {
+        if (type.isUsingGenerics()) {
+            final ClassNode nonGen = ClassHelper.makeWithoutCaching(type.getName());
+            nonGen.setRedirect(type);
+            nonGen.setGenericsTypes(null);
+            nonGen.setUsingGenerics(false);
+            return nonGen;
+        }
+        if (type.isArray() && type.getComponentType().isUsingGenerics()) {
+            return type.getComponentType().getPlainNodeReference().makeArray();
+        }
+        return type;
+    }
+
+    public static ClassNode newClass(ClassNode type) {
+        return type.getPlainNodeReference();
+    }
+
+    public static ClassNode makeClassSafe(Class klass) {
+        return makeClassSafeWithGenerics(ClassHelper.make(klass));
+    }
+
+    public static ClassNode makeClassSafeWithGenerics(Class klass, ClassNode genericsType) {
+        GenericsType[] genericsTypes = new GenericsType[1];
+        genericsTypes[0] = new GenericsType(genericsType);
+        return makeClassSafeWithGenerics(ClassHelper.make(klass), genericsTypes);
+    }
+
+    public static ClassNode makeClassSafe0(ClassNode type, GenericsType... genericTypes) {
+        ClassNode plainNodeReference = newClass(type);
+        if (genericTypes != null && genericTypes.length > 0) {
+            plainNodeReference.setGenericsTypes(genericTypes);
+            if (type.isGenericsPlaceHolder()) plainNodeReference.setGenericsPlaceHolder(true);
+        }
+        return plainNodeReference;
+    }
+
+    public static ClassNode makeClassSafeWithGenerics(ClassNode type, GenericsType... genericTypes) {
+        if (type.isArray()) {
+            return makeClassSafeWithGenerics(type.getComponentType(), genericTypes).makeArray();
+        }
+        GenericsType[] gtypes = new GenericsType[0];
+        if (genericTypes != null) {
+            gtypes = new GenericsType[genericTypes.length];
+            System.arraycopy(genericTypes, 0, gtypes, 0, gtypes.length);
+        }
+        return makeClassSafe0(type, gtypes);
+    }
+
+    public static MethodNode correctToGenericsSpec(Map<String,ClassNode> genericsSpec, MethodNode mn) {
+        ClassNode correctedType = correctToGenericsSpecRecurse(genericsSpec, mn.getReturnType());
+        Parameter[] origParameters = mn.getParameters();
+        Parameter[] newParameters = new Parameter[origParameters.length];
+        for (int i = 0; i < origParameters.length; i++) {
+            Parameter origParameter = origParameters[i];
+            newParameters[i] = new Parameter(correctToGenericsSpecRecurse(genericsSpec, origParameter.getType()), origParameter.getName(), origParameter.getInitialExpression());
+        }
+        return new MethodNode(mn.getName(), mn.getModifiers(), correctedType, newParameters, mn.getExceptions(), mn.getCode());
+    }
+
+    public static ClassNode correctToGenericsSpecRecurse(Map<String,ClassNode> genericsSpec, ClassNode type) {
+        return correctToGenericsSpecRecurse(genericsSpec, type, new ArrayList<String>());
+    }
+
+    /**
+     * @since 2.4.1
+     */
+    public static ClassNode[] correctToGenericsSpecRecurse(Map<String,ClassNode> genericsSpec, ClassNode[] types) {
+        if (types==null || types.length==1) return types;
+        ClassNode[] newTypes = new ClassNode[types.length];
+        boolean modified = false;
+        for (int i=0; i<types.length; i++) {
+            newTypes[i] = correctToGenericsSpecRecurse(genericsSpec, types[i], new ArrayList<String>());
+            modified = modified || (types[i]!=newTypes[i]);
+        }
+        if (!modified) return types;
+        return newTypes;
+    }
+
+    public static ClassNode correctToGenericsSpecRecurse(Map<String,ClassNode> genericsSpec, ClassNode type, List<String> exclusions) {
+        if (type.isArray()) {
+            return correctToGenericsSpecRecurse(genericsSpec, type.getComponentType(), exclusions).makeArray();
+        }
+        if (type.isGenericsPlaceHolder() && !exclusions.contains(type.getUnresolvedName())) {
+            String name = type.getGenericsTypes()[0].getName();
+            type = genericsSpec.get(name);
+            if (type != null && type.isGenericsPlaceHolder() && type.getGenericsTypes() == null) {
+                ClassNode placeholder = ClassHelper.makeWithoutCaching(type.getUnresolvedName());
+                placeholder.setGenericsPlaceHolder(true);
+                type = makeClassSafeWithGenerics(type, new GenericsType(placeholder));
+            }
+        }
+        if (type == null) type = ClassHelper.OBJECT_TYPE;
+        GenericsType[] oldgTypes = type.getGenericsTypes();
+        GenericsType[] newgTypes = GenericsType.EMPTY_ARRAY;
+        if (oldgTypes != null) {
+            newgTypes = new GenericsType[oldgTypes.length];
+            for (int i = 0; i < newgTypes.length; i++) {
+                GenericsType oldgType = oldgTypes[i];
+                if (oldgType.isPlaceholder() ) {
+                    if (genericsSpec.get(oldgType.getName())!=null) {
+                        newgTypes[i] = new GenericsType(genericsSpec.get(oldgType.getName()));
+                    } else {
+                        newgTypes[i] = new GenericsType(ClassHelper.OBJECT_TYPE);
+                    }
+                } else if (oldgType.isWildcard()) {
+                    ClassNode oldLower = oldgType.getLowerBound();
+                    ClassNode lower = oldLower!=null?correctToGenericsSpecRecurse(genericsSpec, oldLower, exclusions):null;
+                    ClassNode[] oldUpper = oldgType.getUpperBounds();
+                    ClassNode[] upper = null;
+                    if (oldUpper!=null) {
+                        upper = new ClassNode[oldUpper.length];
+                        for (int j = 0; j < oldUpper.length; j++) {
+                            upper[j] = correctToGenericsSpecRecurse(genericsSpec,oldUpper[j], exclusions);
+                        }
+                    }
+                    GenericsType fixed = new GenericsType(oldgType.getType(), upper, lower);
+                    fixed.setName(oldgType.getName());
+                    fixed.setWildcard(true);
+                    newgTypes[i] = fixed;
+                } else {
+                    newgTypes[i] = new GenericsType(correctToGenericsSpecRecurse(genericsSpec,correctToGenericsSpec(genericsSpec, oldgType), exclusions));
+                }
+            }
+        }
+        return makeClassSafeWithGenerics(type, newgTypes);
+    }
+
+    public static ClassNode correctToGenericsSpec(Map<String, ClassNode> genericsSpec, GenericsType type) {
+        ClassNode ret = null;
+        if (type.isPlaceholder()) {
+            String name = type.getName();
+            ret = genericsSpec.get(name);
+        }
+        if (ret == null) ret = type.getType();
+        return ret;
+    }
+
+    public static ClassNode correctToGenericsSpec(Map<String,ClassNode> genericsSpec, ClassNode type) {
+        if (type.isArray()) {
+            return correctToGenericsSpec(genericsSpec, type.getComponentType()).makeArray();
+        }
+        if (type.isGenericsPlaceHolder()) {
+            String name = type.getGenericsTypes()[0].getName();
+            type = genericsSpec.get(name);
+        }
+        if (type == null) type = ClassHelper.OBJECT_TYPE;
+        return type;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Map<String,ClassNode> createGenericsSpec(ClassNode current) {
+        return createGenericsSpec(current, Collections.EMPTY_MAP);
+    }
+
+    public static Map<String,ClassNode> createGenericsSpec(ClassNode current, Map<String,ClassNode> oldSpec) {
+        Map<String,ClassNode> ret = new HashMap<String,ClassNode>(oldSpec);
+        // ret contains the type specs, what we now need is the type spec for the
+        // current class. To get that we first apply the type parameters to the
+        // current class and then use the type names of the current class to reset
+        // the map. Example:
+        //   class A<V,W,X>{}
+        //   class B<T extends Number> extends A<T,Long,String> {}
+        // first we have:    T->Number
+        // we apply it to A<T,Long,String> -> A<Number,Long,String>
+        // resulting in:     V->Number,W->Long,X->String
+
+        GenericsType[] sgts = current.getGenericsTypes();
+        if (sgts != null) {
+            ClassNode[] spec = new ClassNode[sgts.length];
+            for (int i = 0; i < spec.length; i++) {
+                spec[i] = correctToGenericsSpec(ret, sgts[i]);
+            }
+            GenericsType[] newGts = current.redirect().getGenericsTypes();
+            if (newGts == null) return ret;
+            ret.clear();
+            for (int i = 0; i < spec.length; i++) {
+                ret.put(newGts[i].getName(), spec[i]);
+            }
+        }
+        return ret;
+    }
+
+    public static Map<String,ClassNode> addMethodGenerics(MethodNode current, Map<String,ClassNode> oldSpec) {
+        Map<String,ClassNode> ret = new HashMap<String,ClassNode>(oldSpec);
+        // ret starts with the original type specs, now add gts for the current method if any
+        GenericsType[] sgts = current.getGenericsTypes();
+        if (sgts != null) {
+            for (GenericsType sgt : sgts) {
+                ret.put(sgt.getName(), sgt.getType());
+            }
+        }
+        return ret;
+    }
+
+    public static void extractSuperClassGenerics(ClassNode type, ClassNode target, Map<String,ClassNode> spec) {
+        // TODO: this method is very similar to StaticTypesCheckingSupport#extractGenericsConnections,
+        // but operates on ClassNodes instead of GenericsType
+        if (target==null || type==target) return;
+        if (type.isArray() && target.isArray()) {
+            extractSuperClassGenerics(type.getComponentType(), target.getComponentType(), spec);
+        } else if (type.isArray() && target.getName().equals("java.lang.Object")) {
+            // Object is superclass of arrays but no generics involved
+        } else if (target.isGenericsPlaceHolder() || type.equals(target) || !StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(type, target)) {
+            // structural match route
+            if (target.isGenericsPlaceHolder()) {
+                spec.put(target.getGenericsTypes()[0].getName(),type);
+            } else {
+                extractSuperClassGenerics(type.getGenericsTypes(), target.getGenericsTypes(), spec);
+            }
+        } else {
+            // have first to find matching super class or interface
+            Map <String,ClassNode> genSpec = createGenericsSpec(type);
+            ClassNode superClass = ClassHelper.getNextSuperClass(type,target);
+            if (superClass!=null){
+                ClassNode corrected = GenericsUtils.correctToGenericsSpecRecurse(genSpec, superClass);
+                extractSuperClassGenerics(corrected, target, spec);
+            } else {
+                // if we reach here, we have an unhandled case 
+                throw new GroovyBugError("The type "+type+" seems not to normally extend "+target+". Sorry, I cannot handle this.");
+            }
+        }
+    }
+
+    private static void extractSuperClassGenerics(GenericsType[] usage, GenericsType[] declaration, Map<String, ClassNode> spec) {
+        // if declaration does not provide generics, there is no connection to make 
+        if (usage==null || declaration==null || declaration.length==0) return;
+        if (usage.length!=declaration.length) return;
+
+        // both have generics
+        for (int i=0; i<usage.length; i++) {
+            GenericsType ui = usage[i];
+            GenericsType di = declaration[i];
+            if (di.isPlaceholder()) {
+                spec.put(di.getName(), ui.getType());
+            } else if (di.isWildcard()){
+                if (ui.isWildcard()) {
+                    extractSuperClassGenerics(ui.getLowerBound(), di.getLowerBound(), spec);
+                    extractSuperClassGenerics(ui.getUpperBounds(), di.getUpperBounds(), spec);
+                } else {
+                    ClassNode cu = ui.getType();
+                    extractSuperClassGenerics(cu, di.getLowerBound(), spec);
+                    ClassNode[] upperBounds = di.getUpperBounds();
+                    if (upperBounds!=null) {
+                        for (ClassNode cn : upperBounds) {
+                            extractSuperClassGenerics(cu, cn, spec);
+                        }
+                    }
+                }
+            } else {
+                extractSuperClassGenerics(ui.getType(), di.getType(), spec);
+            }
+        }
+    }
+
+    private static void extractSuperClassGenerics(ClassNode[] usage, ClassNode[] declaration, Map<String, ClassNode> spec) {
+        if (usage==null || declaration==null || declaration.length==0) return;
+        // both have generics
+        for (int i=0; i<usage.length; i++) {
+            ClassNode ui = usage[i];
+            ClassNode di = declaration[i];
+            if (di.isGenericsPlaceHolder()) {
+                spec.put(di.getGenericsTypes()[0].getName(), di);
+            } else if (di.isUsingGenerics()){
+                extractSuperClassGenerics(ui.getGenericsTypes(), di.getGenericsTypes(), spec);
+            }
+        }
+    }
+
+    public static ClassNode[] parseClassNodesFromString(
+            final String option,
+            final SourceUnit sourceUnit,
+            final CompilationUnit compilationUnit,
+            final MethodNode mn,
+            final ASTNode usage) {
+        GroovyLexer lexer = new GroovyLexer(new StringReader("DummyNode<" + option + ">"));
+        final GroovyRecognizer rn = GroovyRecognizer.make(lexer);
+        try {
+            rn.classOrInterfaceType(true);
+            final AtomicReference<ClassNode> ref = new AtomicReference<ClassNode>();
+            AntlrParserPlugin plugin = new AntlrParserPlugin() {
+                @Override
+                public ModuleNode buildAST(final SourceUnit sourceUnit, final ClassLoader classLoader, final Reduction cst) throws ParserException {
+                    ref.set(makeTypeWithArguments(rn.getAST()));
+                    return null;
+                }
+            };
+            plugin.buildAST(null, null, null);
+            ClassNode parsedNode = ref.get();
+            // the returned node is DummyNode<Param1, Param2, Param3, ...)
+            GenericsType[] parsedNodeGenericsTypes = parsedNode.getGenericsTypes();
+            if (parsedNodeGenericsTypes == null) {
+                return null;
+            }
+            ClassNode[] signature = new ClassNode[parsedNodeGenericsTypes.length];
+            for (int i = 0; i < parsedNodeGenericsTypes.length; i++) {
+                final GenericsType genericsType = parsedNodeGenericsTypes[i];
+                signature[i] = resolveClassNode(sourceUnit, compilationUnit, mn, usage, genericsType.getType());
+            }
+            return signature;
+        } catch (RecognitionException e) {
+            sourceUnit.addError(new IncorrectTypeHintException(mn, e, usage.getLineNumber(), usage.getColumnNumber()));
+        } catch (TokenStreamException e) {
+            sourceUnit.addError(new IncorrectTypeHintException(mn, e, usage.getLineNumber(), usage.getColumnNumber()));
+        } catch (ParserException e) {
+            sourceUnit.addError(new IncorrectTypeHintException(mn, e, usage.getLineNumber(), usage.getColumnNumber()));
+        }
+        return null;
+    }
+
+    private static ClassNode resolveClassNode(final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final MethodNode mn, final ASTNode usage, final ClassNode parsedNode) {
+        ClassNode dummyClass = new ClassNode("dummy",0, ClassHelper.OBJECT_TYPE);
+        dummyClass.setModule(new ModuleNode(sourceUnit));
+        dummyClass.setGenericsTypes(mn.getDeclaringClass().getGenericsTypes());
+        MethodNode dummyMN = new MethodNode(
+                "dummy",
+                0,
+                parsedNode,
+                Parameter.EMPTY_ARRAY,
+                ClassNode.EMPTY_ARRAY,
+                EmptyStatement.INSTANCE
+        );
+        dummyMN.setGenericsTypes(mn.getGenericsTypes());
+        dummyClass.addMethod(dummyMN);
+        ResolveVisitor visitor = new ResolveVisitor(compilationUnit) {
+            @Override
+            public void addError(final String msg, final ASTNode expr) {
+                sourceUnit.addError(new IncorrectTypeHintException(mn, msg, usage.getLineNumber(), usage.getColumnNumber()));
+            }
+        };
+        visitor.startResolving(dummyClass, sourceUnit);
+        return dummyMN.getReturnType();
+    }
+
+    /**
+     * transforms generics types from an old context to a new context using the given spec. This method assumes
+     * all generics types will be placeholders. WARNING: The resulting generics types may or may not be placeholders
+     * after the transformation.
+     * @param genericsSpec the generics context information spec
+     * @param oldPlaceHolders the old placeholders
+     * @return the new generics types
+     */
+    public static GenericsType[] applyGenericsContextToPlaceHolders(Map<String, ClassNode> genericsSpec, GenericsType[] oldPlaceHolders) {
+        if (oldPlaceHolders==null || oldPlaceHolders.length==0) return oldPlaceHolders;
+        if (genericsSpec.isEmpty()) return oldPlaceHolders;
+        GenericsType[] newTypes = new GenericsType[oldPlaceHolders.length];
+        for (int i=0; i<oldPlaceHolders.length; i++) {
+            GenericsType old = oldPlaceHolders[i];
+            if (!old.isPlaceholder()) throw new GroovyBugError("Given generics type "+old+" must be a placeholder!");
+            ClassNode fromSpec = genericsSpec.get(old.getName());
+            if (fromSpec!=null) {
+                if (fromSpec.isGenericsPlaceHolder()) {
+                    ClassNode[] upper = new ClassNode[]{fromSpec.redirect()};
+                    newTypes[i] = new GenericsType(fromSpec, upper, null);
+                } else {
+                    newTypes[i] = new GenericsType(fromSpec);
+                }
+            } else {
+                ClassNode[] upper = old.getUpperBounds();
+                ClassNode[] newUpper = upper;
+                if (upper!=null && upper.length>0) {
+                    ClassNode[] upperCorrected = new ClassNode[upper.length];
+                    for (int j=0;j<upper.length;j++) {
+                        upperCorrected[i] = correctToGenericsSpecRecurse(genericsSpec,upper[j]);
+                    }
+                    upper = upperCorrected;
+                }
+                ClassNode lower = old.getLowerBound();
+                ClassNode newLower = correctToGenericsSpecRecurse(genericsSpec,lower);
+                if (lower==newLower && upper==newUpper) {
+                    newTypes[i] = oldPlaceHolders[i];
+                } else {
+                    ClassNode newPlaceHolder = ClassHelper.make(old.getName());
+                    GenericsType gt = new GenericsType(newPlaceHolder, newUpper, newLower);
+                    gt.setPlaceholder(true);
+                    newTypes[i] = gt;
+                }
+            }
+        }
+        return newTypes;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
new file mode 100644
index 0000000..4edf92e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/tools/ParameterUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.codehaus.groovy.ast.tools;
+
+import org.codehaus.groovy.ast.Parameter;
+
+public class ParameterUtils {
+    public static boolean parametersEqual(Parameter[] a, Parameter[] b) {
+        if (a.length == b.length) {
+            boolean answer = true;
+            for (int i = 0; i < a.length; i++) {
+                if (!a[i].getType().equals(b[i].getType())) {
+                    answer = false;
+                    break;
+                }
+            }
+            return answer;
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
new file mode 100644
index 0000000..513144a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/tools/PropertyNodeUtils.java
@@ -0,0 +1,43 @@
+/*
+ *  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 org.codehaus.groovy.ast.tools;
+
+import org.codehaus.groovy.ast.PropertyNode;
+
+import java.lang.reflect.Modifier;
+
+public class PropertyNodeUtils {
+    /**
+     * Fields within the AST that have no explicit visibility are deemed to be properties
+     * and represented by a PropertyNode. The Groovy compiler creates accessor methods and
+     * a backing field for such property nodes. During this process, all modifiers
+     * from the property are carried over to the backing field (so a property marked as
+     * {@code transient} will have a {@code transient} backing field) but when creating
+     * the accessor methods we don't carry over modifier values which don't make sense for
+     * methods (this includes VOLATILE and TRANSIENT) but other modifiers are carried over,
+     * for example {@code static}.
+     *
+     * @param propNode the original property node
+     * @return the modifiers which make sense for an accessor method
+     */
+    public static int adjustPropertyModifiersForMethod(PropertyNode propNode) {
+        // GROOVY-3726: clear volatile, transient modifiers so that they don't get applied to methods
+        return ~(Modifier.TRANSIENT | Modifier.VOLATILE) & propNode.getModifiers();
+    }
+}